diff --git a/admin/src/App.tsx b/admin/src/App.tsx index 464e2d005..d0edd5e5f 100644 --- a/admin/src/App.tsx +++ b/admin/src/App.tsx @@ -1,4 +1,4 @@ -import { AdminUser } from '@socialincome/shared/src/types/AdminUser'; +import { AdminUser } from '@socialincome/shared/src/types/admin-user'; import algoliasearch from 'algoliasearch'; import { Authenticator, diff --git a/admin/src/actions/CreateDonationCertificatesAction.tsx b/admin/src/actions/CreateDonationCertificatesAction.tsx index c5bdf83da..ae2211d6d 100644 --- a/admin/src/actions/CreateDonationCertificatesAction.tsx +++ b/admin/src/actions/CreateDonationCertificatesAction.tsx @@ -10,13 +10,13 @@ import { Select, Typography, } from '@mui/material'; +import { DEFAULT_REGION } from '@socialincome/shared/src/firebase'; +import { User } from '@socialincome/shared/src/types/user'; import { getFunctions, httpsCallable } from 'firebase/functions'; import { CollectionActionsProps, useAuthController, useSnackbarController } from 'firecms'; import _ from 'lodash'; import React from 'react'; import { CreateDonationCertificatesFunctionProps } from '../../../functions/src/webhooks/admin/donation-certificates/DonationCertificateHandler'; -import { DEFAULT_REGION } from '../../../shared/src/firebase'; -import { User } from '../../../shared/src/types/User'; const style = { position: 'absolute' as 'absolute', diff --git a/admin/src/actions/InviteWhatsappAction.tsx b/admin/src/actions/InviteWhatsappAction.tsx index a62206216..61547a57c 100644 --- a/admin/src/actions/InviteWhatsappAction.tsx +++ b/admin/src/actions/InviteWhatsappAction.tsx @@ -1,10 +1,10 @@ import { Box, Button, Modal, Typography } from '@mui/material'; +import { DEFAULT_REGION } from '@socialincome/shared/src/firebase'; +import { Recipient } from '@socialincome/shared/src/types/recipient'; import { getFunctions, httpsCallable } from 'firebase/functions'; import { CollectionActionsProps, useAuthController, useSnackbarController } from 'firecms'; import React from 'react'; import { TwilioOutgoingMessageFunctionProps } from '../../../functions/src/webhooks/twilio/TwilioOutgoingMessageHandler'; -import { DEFAULT_REGION } from '../../../shared/src/firebase'; -import { Recipient } from '../../../shared/src/types/Recipient'; const STYLE = { position: 'absolute' as 'absolute', diff --git a/admin/src/actions/PaymentProcessAction.tsx b/admin/src/actions/PaymentProcessAction.tsx index ae7c9ca10..4c9651fea 100644 --- a/admin/src/actions/PaymentProcessAction.tsx +++ b/admin/src/actions/PaymentProcessAction.tsx @@ -1,8 +1,8 @@ import { Box, Button, CircularProgress, Modal, Tooltip, Typography } from '@mui/material'; import { DatePicker } from '@mui/x-date-pickers/DatePicker'; import { DEFAULT_REGION } from '@socialincome/shared/src/firebase'; -import { PaymentProcessTaskType } from '@socialincome/shared/src/types/Payment'; -import { toPaymentDate } from '@socialincome/shared/src/types/Recipient'; +import { PaymentProcessTaskType } from '@socialincome/shared/src/types/payment'; +import { toPaymentDate } from '@socialincome/shared/src/types/recipient'; import { downloadStringAsFile } from '@socialincome/shared/src/utils/html'; import { getFunctions, httpsCallable } from 'firebase/functions'; import { useSnackbarController } from 'firecms'; diff --git a/admin/src/collections/Admins.ts b/admin/src/collections/Admins.ts index 375c146bd..1197292d7 100644 --- a/admin/src/collections/Admins.ts +++ b/admin/src/collections/Admins.ts @@ -1,5 +1,5 @@ -import { ADMIN_USER_FIRESTORE_PATH, AdminUser } from '@socialincome/shared/src/types/AdminUser'; -import { PARTNER_ORGANISATION_FIRESTORE_PATH } from '@socialincome/shared/src/types/PartnerOrganisation'; +import { ADMIN_USER_FIRESTORE_PATH, AdminUser } from '@socialincome/shared/src/types/admin-user'; +import { PARTNER_ORGANISATION_FIRESTORE_PATH } from '@socialincome/shared/src/types/partner-organisation'; import { buildProperties } from 'firecms'; import { buildAuditedCollection } from './shared'; diff --git a/admin/src/collections/Contributions.ts b/admin/src/collections/Contributions.ts index ce96b1cb9..7b5154715 100644 --- a/admin/src/collections/Contributions.ts +++ b/admin/src/collections/Contributions.ts @@ -3,7 +3,7 @@ import { Contribution, ContributionSourceKey, StatusKey, -} from '@socialincome/shared/src/types/Contribution'; +} from '@socialincome/shared/src/types/contribution'; import { buildProperties } from 'firecms'; import { EntityCollection } from 'firecms/dist/types/collections'; import { buildAuditedCollection } from './shared'; diff --git a/admin/src/collections/DonationCertificate.tsx b/admin/src/collections/DonationCertificate.tsx index 8f6459e0c..283d55205 100644 --- a/admin/src/collections/DonationCertificate.tsx +++ b/admin/src/collections/DonationCertificate.tsx @@ -1,7 +1,7 @@ import { DONATION_CERTIFICATE_FIRESTORE_PATH, DonationCertificate, -} from '@socialincome/shared/src/types/DonationCertificate'; +} from '@socialincome/shared/src/types/donation-certificate'; import { AdditionalFieldDelegate, buildProperties } from 'firecms'; import { buildAuditedCollection } from './shared'; diff --git a/admin/src/collections/Messages.ts b/admin/src/collections/Messages.ts index d30873c79..7e6f1c739 100644 --- a/admin/src/collections/Messages.ts +++ b/admin/src/collections/Messages.ts @@ -1,4 +1,4 @@ -import { Email, MESSAGE_FIRESTORE_PATH, TwilioMessage } from '@socialincome/shared/src/types/Message'; +import { Email, MESSAGE_FIRESTORE_PATH, TwilioMessage } from '@socialincome/shared/src/types/message'; import { buildProperties } from 'firecms'; import { buildAuditedCollection } from './shared'; diff --git a/admin/src/collections/OperationalExpenses.ts b/admin/src/collections/OperationalExpenses.ts index f7db807ed..264473ad6 100644 --- a/admin/src/collections/OperationalExpenses.ts +++ b/admin/src/collections/OperationalExpenses.ts @@ -1,7 +1,7 @@ import { OPERATIONAL_EXPENSE_FIRESTORE_PATH, OperationalExpense, -} from '@socialincome/shared/src/types/OperationalExpense'; +} from '@socialincome/shared/src/types/operational-expense'; import { buildProperties } from 'firecms'; import { buildAuditedCollection } from './shared'; @@ -13,11 +13,11 @@ export const operationalExpensesCollection = buildAuditedCollection ({ + permissions: { edit: true, create: true, delete: true, - }), + }, properties: buildProperties({ name: { dataType: 'string', diff --git a/admin/src/collections/PartnerOrganisations.ts b/admin/src/collections/PartnerOrganisations.ts index b5931abf6..8ea2ca81a 100644 --- a/admin/src/collections/PartnerOrganisations.ts +++ b/admin/src/collections/PartnerOrganisations.ts @@ -1,7 +1,7 @@ import { PARTNER_ORGANISATION_FIRESTORE_PATH, PartnerOrganisation, -} from '@socialincome/shared/src/types/PartnerOrganisation'; +} from '@socialincome/shared/src/types/partner-organisation'; import { buildProperties } from 'firecms'; import { buildAuditedCollection } from './shared'; diff --git a/admin/src/collections/Payments.ts b/admin/src/collections/Payments.ts index dc95e545a..ed9e9ded1 100644 --- a/admin/src/collections/Payments.ts +++ b/admin/src/collections/Payments.ts @@ -1,5 +1,5 @@ -import { MESSAGE_FIRESTORE_PATH } from '@socialincome/shared/src/types/Message'; -import { Payment, PAYMENT_FIRESTORE_PATH, PaymentStatus } from '@socialincome/shared/src/types/Payment'; +import { MESSAGE_FIRESTORE_PATH } from '@socialincome/shared/src/types/message'; +import { Payment, PAYMENT_FIRESTORE_PATH, PaymentStatus } from '@socialincome/shared/src/types/payment'; import { buildProperties, EnumValues } from 'firecms'; import { buildAuditedCollection } from './shared'; diff --git a/admin/src/collections/Users.tsx b/admin/src/collections/Users.tsx index 9ce446cbe..a6f2457fb 100644 --- a/admin/src/collections/Users.tsx +++ b/admin/src/collections/Users.tsx @@ -1,4 +1,4 @@ -import { USER_FIRESTORE_PATH, User, UserReferralSource } from '@socialincome/shared/src/types/User'; +import { USER_FIRESTORE_PATH, User, UserReferralSource } from '@socialincome/shared/src/types/user'; import { AdditionalFieldDelegate, buildProperties } from 'firecms'; import { CreateDonationCertificatesAction } from '../actions/CreateDonationCertificatesAction'; import { buildContributionsCollection } from './Contributions'; diff --git a/admin/src/collections/recipients/Recipients.tsx b/admin/src/collections/recipients/Recipients.tsx index b32fb1ba4..64eb7854b 100644 --- a/admin/src/collections/recipients/Recipients.tsx +++ b/admin/src/collections/recipients/Recipients.tsx @@ -4,7 +4,7 @@ import { Recipient, calcFinalPaymentDate, calcPaymentsLeft, -} from '@socialincome/shared/src/types/Recipient'; +} from '@socialincome/shared/src/types/recipient'; import { toDateTime } from '@socialincome/shared/src/utils/date'; import { AdditionalFieldDelegate, buildProperties } from 'firecms'; import { EntityCollection, PropertiesOrBuilders } from 'firecms/dist/types'; diff --git a/admin/src/collections/recipients/RecipientsPayments.tsx b/admin/src/collections/recipients/RecipientsPayments.tsx index 48228ec58..39f90a897 100644 --- a/admin/src/collections/recipients/RecipientsPayments.tsx +++ b/admin/src/collections/recipients/RecipientsPayments.tsx @@ -1,5 +1,5 @@ -import { Payment } from '@socialincome/shared/src/types/Payment'; -import { RECIPIENT_FIRESTORE_PATH, Recipient, RecipientProgramStatus } from '@socialincome/shared/src/types/Recipient'; +import { Payment } from '@socialincome/shared/src/types/payment'; +import { RECIPIENT_FIRESTORE_PATH, Recipient, RecipientProgramStatus } from '@socialincome/shared/src/types/recipient'; import { getMonthIDs } from '@socialincome/shared/src/utils/date'; import { AdditionalFieldDelegate, diff --git a/admin/src/collections/recipients/RecipientsProperties.ts b/admin/src/collections/recipients/RecipientsProperties.ts index 55c3f37b1..ed73a6ddb 100644 --- a/admin/src/collections/recipients/RecipientsProperties.ts +++ b/admin/src/collections/recipients/RecipientsProperties.ts @@ -1,5 +1,5 @@ -import { PARTNER_ORGANISATION_FIRESTORE_PATH } from '@socialincome/shared/src/types/PartnerOrganisation'; -import { RecipientMainLanguage, RecipientProgramStatus } from '@socialincome/shared/src/types/Recipient'; +import { PARTNER_ORGANISATION_FIRESTORE_PATH } from '@socialincome/shared/src/types/partner-organisation'; +import { RecipientMainLanguage, RecipientProgramStatus } from '@socialincome/shared/src/types/recipient'; import { Property, StringProperty } from 'firecms/dist/types/properties'; export const programStatusProperty: Property = { diff --git a/admin/src/collections/surveys/Surveys.tsx b/admin/src/collections/surveys/Surveys.tsx index 624a6bbdf..6b8087ed4 100644 --- a/admin/src/collections/surveys/Surveys.tsx +++ b/admin/src/collections/surveys/Surveys.tsx @@ -1,5 +1,5 @@ +import { SURVEY_FIRETORE_PATH, Survey } from '@socialincome/shared/src/types/survey'; import { EntityCollection } from 'firecms/dist/types/collections'; -import { SURVEY_FIRETORE_PATH, Survey } from '../../../../shared/src/types/Survey'; import { buildAuditedCollection } from '../shared'; import { accessEmailProperty, diff --git a/admin/src/collections/surveys/SurveysProperties.ts b/admin/src/collections/surveys/SurveysProperties.ts index 10c00a225..cc998b302 100644 --- a/admin/src/collections/surveys/SurveysProperties.ts +++ b/admin/src/collections/surveys/SurveysProperties.ts @@ -1,5 +1,5 @@ -import { RecipientMainLanguage } from '@socialincome/shared/src/types/Recipient'; -import { SurveyQuestionnaire, SurveyStatus } from '@socialincome/shared/src/types/Survey'; +import { RecipientMainLanguage } from '@socialincome/shared/src/types/recipient'; +import { SurveyQuestionnaire, SurveyStatus } from '@socialincome/shared/src/types/survey'; import { DateProperty, MapProperty, Property, StringProperty } from 'firecms/dist/types/properties'; export const surveyStatusProperty: StringProperty = { diff --git a/admin/src/views/NextSurveysView.tsx b/admin/src/views/NextSurveysView.tsx index be4ecefea..d6055a718 100644 --- a/admin/src/views/NextSurveysView.tsx +++ b/admin/src/views/NextSurveysView.tsx @@ -1,7 +1,7 @@ import { Box, Button, Link, Popover, Typography } from '@mui/material'; import { DataGrid } from '@mui/x-data-grid'; -import { Recipient } from '@socialincome/shared/src/types/Recipient'; -import { Survey, SurveyStatus, getSurveyUrl } from '@socialincome/shared/src/types/Survey'; +import { Recipient } from '@socialincome/shared/src/types/recipient'; +import { Survey, SurveyStatus, getSurveyUrl } from '@socialincome/shared/src/types/survey'; import { toDate, toDateTime } from '@socialincome/shared/src/utils/date'; import { QueryDocumentSnapshot, diff --git a/admin/src/views/PaymentsConfirmationView.tsx b/admin/src/views/PaymentsConfirmationView.tsx index c0456293d..f04d6bb92 100644 --- a/admin/src/views/PaymentsConfirmationView.tsx +++ b/admin/src/views/PaymentsConfirmationView.tsx @@ -1,7 +1,7 @@ import { Box, Button, Link, Popover, Typography } from '@mui/material'; import { DataGrid } from '@mui/x-data-grid'; -import { Payment, PaymentStatus } from '@socialincome/shared/src/types/Payment'; -import { Recipient } from '@socialincome/shared/src/types/Recipient'; +import { Payment, PaymentStatus } from '@socialincome/shared/src/types/payment'; +import { Recipient } from '@socialincome/shared/src/types/recipient'; import { toDateTime } from '@socialincome/shared/src/utils/date'; import { QueryDocumentSnapshot, diff --git a/functions/src/cron/exchange-rate-import/ExchangeRateImporter.test.ts b/functions/src/cron/exchange-rate-import/ExchangeRateImporter.test.ts index 3464c4ca8..9d9efdf72 100644 --- a/functions/src/cron/exchange-rate-import/ExchangeRateImporter.test.ts +++ b/functions/src/cron/exchange-rate-import/ExchangeRateImporter.test.ts @@ -2,7 +2,7 @@ import { beforeEach, describe, expect, test } from '@jest/globals'; import functions from 'firebase-functions-test'; import { FirestoreAdmin } from '../../../../shared/src/firebase/admin/FirestoreAdmin'; import { getOrInitializeFirebaseAdmin } from '../../../../shared/src/firebase/admin/app'; -import { EXCHANGE_RATES_PATH, ExchangeRates, ExchangeRatesEntry } from '../../../../shared/src/types/ExchangeRates'; +import { EXCHANGE_RATES_PATH, ExchangeRates, ExchangeRatesEntry } from '../../../../shared/src/types/exchange-rates'; import { ExchangeRateImporter, ExchangeRateResponse } from './ExchangeRateImporter'; describe('importExchangeRates', () => { diff --git a/functions/src/cron/exchange-rate-import/ExchangeRateImporter.ts b/functions/src/cron/exchange-rate-import/ExchangeRateImporter.ts index 70a12e73b..5c15d4688 100644 --- a/functions/src/cron/exchange-rate-import/ExchangeRateImporter.ts +++ b/functions/src/cron/exchange-rate-import/ExchangeRateImporter.ts @@ -2,7 +2,7 @@ import axios from 'axios'; import { logger } from 'firebase-functions'; import { DateTime } from 'luxon'; import { FirestoreAdmin } from '../../../../shared/src/firebase/admin/FirestoreAdmin'; -import { EXCHANGE_RATES_PATH, ExchangeRates, ExchangeRatesEntry } from '../../../../shared/src/types/ExchangeRates'; +import { EXCHANGE_RATES_PATH, ExchangeRates, ExchangeRatesEntry } from '../../../../shared/src/types/exchange-rates'; import { EXCHANGE_RATES_API } from '../../config'; export type ExchangeRateResponse = { diff --git a/functions/src/cron/postfinance-balance-import/PostFinanceBalanceImporter.test.ts b/functions/src/cron/postfinance-balance-import/PostFinanceBalanceImporter.test.ts index 6099d85e8..18adacd5f 100644 --- a/functions/src/cron/postfinance-balance-import/PostFinanceBalanceImporter.test.ts +++ b/functions/src/cron/postfinance-balance-import/PostFinanceBalanceImporter.test.ts @@ -6,7 +6,7 @@ import { BANK_BALANCE_FIRESTORE_PATH, BankBalance, getIdFromBankBalance, -} from '../../../../shared/src/types/BankBalance'; +} from '../../../../shared/src/types/bank-balance'; import { PostFinanceBalanceImporter } from './PostFinanceBalanceImporter'; describe('importPostfinanceBalance', () => { diff --git a/functions/src/cron/postfinance-balance-import/PostFinanceBalanceImporter.ts b/functions/src/cron/postfinance-balance-import/PostFinanceBalanceImporter.ts index fd90d2756..ff4f5952a 100644 --- a/functions/src/cron/postfinance-balance-import/PostFinanceBalanceImporter.ts +++ b/functions/src/cron/postfinance-balance-import/PostFinanceBalanceImporter.ts @@ -7,7 +7,7 @@ import { BANK_BALANCE_FIRESTORE_PATH, BankBalance, getIdFromBankBalance, -} from '../../../../shared/src/types/BankBalance'; +} from '../../../../shared/src/types/bank-balance'; import { POSTFINANCE_EMAIL_PASSWORD, POSTFINANCE_EMAIL_USER } from '../../config'; export class PostFinanceBalanceImporter { diff --git a/functions/src/firebase.ts b/functions/src/firebase.ts index c4d8f7ca2..8f3283c41 100644 --- a/functions/src/firebase.ts +++ b/functions/src/firebase.ts @@ -1,9 +1,9 @@ import { getOrInitializeFirebaseAdmin } from '../../shared/src/firebase/admin/app'; import { FirestoreAdmin } from '../../shared/src/firebase/admin/FirestoreAdmin'; -import { ADMIN_USER_FIRESTORE_PATH, AdminUser } from '../../shared/src/types/AdminUser'; -import { EXCHANGE_RATES_PATH, ExchangeRatesEntry } from '../../shared/src/types/ExchangeRates'; -import { PARTNER_ORGANISATION_FIRESTORE_PATH, PartnerOrganisation } from '../../shared/src/types/PartnerOrganisation'; -import { Recipient, RECIPIENT_FIRESTORE_PATH, RecipientProgramStatus } from '../../shared/src/types/Recipient'; +import { ADMIN_USER_FIRESTORE_PATH, AdminUser } from '../../shared/src/types/admin-user'; +import { EXCHANGE_RATES_PATH, ExchangeRatesEntry } from '../../shared/src/types/exchange-rates'; +import { PARTNER_ORGANISATION_FIRESTORE_PATH, PartnerOrganisation } from '../../shared/src/types/partner-organisation'; +import { Recipient, RECIPIENT_FIRESTORE_PATH, RecipientProgramStatus } from '../../shared/src/types/recipient'; export async function initializeGlobalTestData(projectId?: string) { const firestoreAdmin = new FirestoreAdmin(getOrInitializeFirebaseAdmin({ projectId })); diff --git a/functions/src/storage/postfinance-payments-files/PostfinancePaymentsFileImporter.ts b/functions/src/storage/postfinance-payments-files/PostfinancePaymentsFileImporter.ts index 80c0875aa..6e32c9084 100644 --- a/functions/src/storage/postfinance-payments-files/PostfinancePaymentsFileImporter.ts +++ b/functions/src/storage/postfinance-payments-files/PostfinancePaymentsFileImporter.ts @@ -9,8 +9,8 @@ import { CONTRIBUTION_FIRESTORE_PATH, ContributionSourceKey, StatusKey, -} from '../../../../shared/src/types/Contribution'; -import { USER_FIRESTORE_PATH, User } from '../../../../shared/src/types/User'; +} from '../../../../shared/src/types/contribution'; +import { USER_FIRESTORE_PATH, User } from '../../../../shared/src/types/user'; // TODO: write tests export class PostfinancePaymentsFileImporter { diff --git a/functions/src/webhooks/admin/donation-certificates/DonationCertificateHandler.ts b/functions/src/webhooks/admin/donation-certificates/DonationCertificateHandler.ts index 1f6208491..4e2ad43c3 100644 --- a/functions/src/webhooks/admin/donation-certificates/DonationCertificateHandler.ts +++ b/functions/src/webhooks/admin/donation-certificates/DonationCertificateHandler.ts @@ -6,9 +6,9 @@ import { withFile } from 'tmp-promise'; import { FirestoreAdmin } from '../../../../../shared/src/firebase/admin/FirestoreAdmin'; import { StorageAdmin } from '../../../../../shared/src/firebase/admin/StorageAdmin'; import { Entity } from '../../../../../shared/src/types'; -import { Contribution, StatusKey } from '../../../../../shared/src/types/Contribution'; -import { DonationCertificate } from '../../../../../shared/src/types/DonationCertificate'; -import { User } from '../../../../../shared/src/types/User'; +import { Contribution, StatusKey } from '../../../../../shared/src/types/contribution'; +import { DonationCertificate } from '../../../../../shared/src/types/donation-certificate'; +import { User } from '../../../../../shared/src/types/user'; import { Translator } from '../../../../../shared/src/utils/i18n'; import { sendEmail } from '../../../../../shared/src/utils/messaging/email'; import { renderTemplate } from '../../../../../shared/src/utils/templates'; diff --git a/functions/src/webhooks/admin/payment-process/index.ts b/functions/src/webhooks/admin/payment-process/index.ts index 98d89bd46..6298a6a18 100644 --- a/functions/src/webhooks/admin/payment-process/index.ts +++ b/functions/src/webhooks/admin/payment-process/index.ts @@ -2,8 +2,8 @@ import * as functions from 'firebase-functions'; import { DateTime } from 'luxon'; import { DEFAULT_REGION } from '../../../../../shared/src/firebase'; import { FirestoreAdmin } from '../../../../../shared/src/firebase/admin/FirestoreAdmin'; -import { PaymentProcessTaskType } from '../../../../../shared/src/types/Payment'; -import { toPaymentDate } from '../../../../../shared/src/types/Recipient'; +import { PaymentProcessTaskType } from '../../../../../shared/src/types/payment'; +import { toPaymentDate } from '../../../../../shared/src/types/recipient'; import { PaymentCSVTask } from './tasks/PaymentCSVTask'; import { PaymentTask } from './tasks/PaymentTask'; import { RegistrationCSVTask } from './tasks/RegistrationCSVTask'; diff --git a/functions/src/webhooks/admin/payment-process/tasks/PaymentCSVTask.test.ts b/functions/src/webhooks/admin/payment-process/tasks/PaymentCSVTask.test.ts index 7509934b9..dcdbf7a84 100644 --- a/functions/src/webhooks/admin/payment-process/tasks/PaymentCSVTask.test.ts +++ b/functions/src/webhooks/admin/payment-process/tasks/PaymentCSVTask.test.ts @@ -1,7 +1,7 @@ import functionsTest from 'firebase-functions-test'; import { DateTime } from 'luxon'; -import { PaymentProcessTaskType } from '../../../../../../shared/src/types/Payment'; -import { toPaymentDate } from '../../../../../../shared/src/types/Recipient'; +import { PaymentProcessTaskType } from '../../../../../../shared/src/types/payment'; +import { toPaymentDate } from '../../../../../../shared/src/types/recipient'; import { initializeGlobalTestData } from '../../../../firebase'; import { runPaymentProcessTask } from '../../../index'; diff --git a/functions/src/webhooks/admin/payment-process/tasks/PaymentCSVTask.ts b/functions/src/webhooks/admin/payment-process/tasks/PaymentCSVTask.ts index 2dae43ef3..e5fe320a4 100644 --- a/functions/src/webhooks/admin/payment-process/tasks/PaymentCSVTask.ts +++ b/functions/src/webhooks/admin/payment-process/tasks/PaymentCSVTask.ts @@ -1,5 +1,5 @@ import { DateTime } from 'luxon'; -import { PAYMENT_AMOUNT } from '../../../../../../shared/src/types/Payment'; +import { PAYMENT_AMOUNT } from '../../../../../../shared/src/types/payment'; import { PaymentTask } from './PaymentTask'; export class PaymentCSVTask extends PaymentTask { diff --git a/functions/src/webhooks/admin/payment-process/tasks/PaymentTask.ts b/functions/src/webhooks/admin/payment-process/tasks/PaymentTask.ts index a1d743e20..e5ed151f3 100644 --- a/functions/src/webhooks/admin/payment-process/tasks/PaymentTask.ts +++ b/functions/src/webhooks/admin/payment-process/tasks/PaymentTask.ts @@ -5,7 +5,7 @@ import { Recipient, RECIPIENT_FIRESTORE_PATH, RecipientProgramStatus, -} from '../../../../../../shared/src/types/Recipient'; +} from '../../../../../../shared/src/types/recipient'; import QueryDocumentSnapshot = firestore.QueryDocumentSnapshot; export abstract class PaymentTask { diff --git a/functions/src/webhooks/admin/payment-process/tasks/RegistrationCSVTask.test.ts b/functions/src/webhooks/admin/payment-process/tasks/RegistrationCSVTask.test.ts index 11611c926..2142ec0f8 100644 --- a/functions/src/webhooks/admin/payment-process/tasks/RegistrationCSVTask.test.ts +++ b/functions/src/webhooks/admin/payment-process/tasks/RegistrationCSVTask.test.ts @@ -1,7 +1,7 @@ import functionsTest from 'firebase-functions-test'; import { DateTime } from 'luxon'; -import { PaymentProcessTaskType } from '../../../../../../shared/src/types/Payment'; -import { toPaymentDate } from '../../../../../../shared/src/types/Recipient'; +import { PaymentProcessTaskType } from '../../../../../../shared/src/types/payment'; +import { toPaymentDate } from '../../../../../../shared/src/types/recipient'; import { initializeGlobalTestData } from '../../../../firebase'; import { runPaymentProcessTask } from '../../../index'; diff --git a/functions/src/webhooks/admin/payment-process/tasks/SendNotificationsTask.ts b/functions/src/webhooks/admin/payment-process/tasks/SendNotificationsTask.ts index d3cbb2a79..179eef077 100644 --- a/functions/src/webhooks/admin/payment-process/tasks/SendNotificationsTask.ts +++ b/functions/src/webhooks/admin/payment-process/tasks/SendNotificationsTask.ts @@ -1,9 +1,8 @@ import { DateTime } from 'luxon'; import { MessageInstance } from 'twilio/lib/rest/api/v2010/account/message'; -import {} from '../../../../../../shared/src/types'; -import { MESSAGE_FIRESTORE_PATH, MessageType, TwilioMessage } from '../../../../../../shared/src/types/Message'; -import { PAYMENT_FIRESTORE_PATH, Payment } from '../../../../../../shared/src/types/Payment'; -import { RECIPIENT_FIRESTORE_PATH, Recipient } from '../../../../../../shared/src/types/Recipient'; +import { MESSAGE_FIRESTORE_PATH, MessageType, TwilioMessage } from '../../../../../../shared/src/types/message'; +import { PAYMENT_FIRESTORE_PATH, Payment } from '../../../../../../shared/src/types/payment'; +import { RECIPIENT_FIRESTORE_PATH, Recipient } from '../../../../../../shared/src/types/recipient'; import { sendSms } from '../../../../../../shared/src/utils/messaging/sms'; import { TWILIO_SENDER_PHONE, TWILIO_SID, TWILIO_TOKEN } from '../../../../config'; import { PaymentTask } from './PaymentTask'; diff --git a/functions/src/webhooks/admin/payment-process/tasks/UpdateDatabaseEntriesTask.test.ts b/functions/src/webhooks/admin/payment-process/tasks/UpdateDatabaseEntriesTask.test.ts index 2ee61b7e2..c981e9491 100644 --- a/functions/src/webhooks/admin/payment-process/tasks/UpdateDatabaseEntriesTask.test.ts +++ b/functions/src/webhooks/admin/payment-process/tasks/UpdateDatabaseEntriesTask.test.ts @@ -7,13 +7,13 @@ import { PAYMENT_FIRESTORE_PATH, PaymentProcessTaskType, PaymentStatus, -} from '../../../../../../shared/src/types/Payment'; +} from '../../../../../../shared/src/types/payment'; import { Recipient, RECIPIENT_FIRESTORE_PATH, RecipientProgramStatus, toPaymentDate, -} from '../../../../../../shared/src/types/Recipient'; +} from '../../../../../../shared/src/types/recipient'; import { toDateTime } from '../../../../../../shared/src/utils/date'; import { initializeGlobalTestData } from '../../../../firebase'; import { runPaymentProcessTask } from '../../../index'; diff --git a/functions/src/webhooks/admin/payment-process/tasks/UpdateDatabaseEntriesTask.ts b/functions/src/webhooks/admin/payment-process/tasks/UpdateDatabaseEntriesTask.ts index 081d5af26..491d7a954 100644 --- a/functions/src/webhooks/admin/payment-process/tasks/UpdateDatabaseEntriesTask.ts +++ b/functions/src/webhooks/admin/payment-process/tasks/UpdateDatabaseEntriesTask.ts @@ -7,8 +7,8 @@ import { PAYMENT_FIRESTORE_PATH, Payment, PaymentStatus, -} from '../../../../../../shared/src/types/Payment'; -import { RECIPIENT_FIRESTORE_PATH, RecipientProgramStatus } from '../../../../../../shared/src/types/Recipient'; +} from '../../../../../../shared/src/types/payment'; +import { RECIPIENT_FIRESTORE_PATH, RecipientProgramStatus } from '../../../../../../shared/src/types/recipient'; import { ExchangeRateImporter } from '../../../../cron/exchange-rate-import/ExchangeRateImporter'; import { PaymentTask } from './PaymentTask'; diff --git a/functions/src/webhooks/admin/scripts/BatchAddCHFToPayments.test.ts b/functions/src/webhooks/admin/scripts/BatchAddCHFToPayments.test.ts index 5898fb421..b7f2cf162 100644 --- a/functions/src/webhooks/admin/scripts/BatchAddCHFToPayments.test.ts +++ b/functions/src/webhooks/admin/scripts/BatchAddCHFToPayments.test.ts @@ -1,7 +1,7 @@ import { DateTime } from 'luxon'; import { toFirebaseAdminTimestamp } from '../../../../../shared/src/firebase/admin/utils'; -import { ExchangeRates } from '../../../../../shared/src/types/ExchangeRates'; -import { Payment, PaymentStatus } from '../../../../../shared/src/types/Payment'; +import { ExchangeRates } from '../../../../../shared/src/types/exchange-rates'; +import { Payment, PaymentStatus } from '../../../../../shared/src/types/payment'; import { PaymentsManager } from './PaymentsManager'; test('BatchAddCHFToPayments', async () => { diff --git a/functions/src/webhooks/admin/scripts/PaymentsManager.ts b/functions/src/webhooks/admin/scripts/PaymentsManager.ts index e08d7a847..8b9e26e7f 100644 --- a/functions/src/webhooks/admin/scripts/PaymentsManager.ts +++ b/functions/src/webhooks/admin/scripts/PaymentsManager.ts @@ -1,6 +1,6 @@ import { FirestoreAdmin } from '../../../../../shared/src/firebase/admin/FirestoreAdmin'; -import { ExchangeRates } from '../../../../../shared/src/types/ExchangeRates'; -import { Payment, PAYMENT_FIRESTORE_PATH } from '../../../../../shared/src/types/Payment'; +import { ExchangeRates } from '../../../../../shared/src/types/exchange-rates'; +import { Payment, PAYMENT_FIRESTORE_PATH } from '../../../../../shared/src/types/payment'; import { ExchangeRateImporter } from '../../../cron/exchange-rate-import/ExchangeRateImporter'; export class PaymentsManager { diff --git a/functions/src/webhooks/admin/scripts/SurveyManager.ts b/functions/src/webhooks/admin/scripts/SurveyManager.ts index 897040db0..d9cf29165 100644 --- a/functions/src/webhooks/admin/scripts/SurveyManager.ts +++ b/functions/src/webhooks/admin/scripts/SurveyManager.ts @@ -7,14 +7,14 @@ import { Recipient, RecipientMainLanguage, RecipientProgramStatus, -} from '../../../../../shared/src/types/Recipient'; +} from '../../../../../shared/src/types/recipient'; import { SURVEY_FIRETORE_PATH, Survey, SurveyQuestionnaire, SurveyStatus, recipientSurveys, -} from '../../../../../shared/src/types/Survey'; +} from '../../../../../shared/src/types/survey'; import { rndString } from '../../../../../shared/src/utils/crypto'; import { toDateTime } from '../../../../../shared/src/utils/date'; diff --git a/functions/src/webhooks/twilio/TwilioIncomingMessageHandler.ts b/functions/src/webhooks/twilio/TwilioIncomingMessageHandler.ts index a81278c60..822df8ba1 100644 --- a/functions/src/webhooks/twilio/TwilioIncomingMessageHandler.ts +++ b/functions/src/webhooks/twilio/TwilioIncomingMessageHandler.ts @@ -1,7 +1,7 @@ import * as functions from 'firebase-functions'; import { FirestoreAdmin } from '../../../../shared/src/firebase/admin/FirestoreAdmin'; -import { MESSAGE_FIRESTORE_PATH, MessageType, TwilioMessage } from '../../../../shared/src/types/Message'; -import { RECIPIENT_FIRESTORE_PATH, Recipient } from '../../../../shared/src/types/Recipient'; +import { MESSAGE_FIRESTORE_PATH, MessageType, TwilioMessage } from '../../../../shared/src/types/message'; +import { RECIPIENT_FIRESTORE_PATH, Recipient } from '../../../../shared/src/types/recipient'; export class TwilioIncomingMessageHandler { /** diff --git a/functions/src/webhooks/twilio/TwilioOutgoingMessageHandler.ts b/functions/src/webhooks/twilio/TwilioOutgoingMessageHandler.ts index 530819fae..800f50943 100644 --- a/functions/src/webhooks/twilio/TwilioOutgoingMessageHandler.ts +++ b/functions/src/webhooks/twilio/TwilioOutgoingMessageHandler.ts @@ -2,8 +2,8 @@ import * as functions from 'firebase-functions'; import { MessageInstance } from 'twilio/lib/rest/api/v2010/account/message'; import { FirestoreAdmin } from '../../../../shared/src/firebase/admin/FirestoreAdmin'; import { Entity } from '../../../../shared/src/types'; -import { MESSAGE_FIRESTORE_PATH, MessageType, TwilioMessage } from '../../../../shared/src/types/Message'; -import { RECIPIENT_FIRESTORE_PATH, Recipient } from '../../../../shared/src/types/Recipient'; +import { MESSAGE_FIRESTORE_PATH, MessageType, TwilioMessage } from '../../../../shared/src/types/message'; +import { RECIPIENT_FIRESTORE_PATH, Recipient } from '../../../../shared/src/types/recipient'; import { sendWhatsapp } from '../../../../shared/src/utils/messaging/whatsapp'; import { TWILIO_SENDER_PHONE, TWILIO_SID, TWILIO_TOKEN } from '../../config'; diff --git a/functions/src/webhooks/website/survey-login/index.ts b/functions/src/webhooks/website/survey-login/index.ts index bb78b6b3f..4c2a489e3 100644 --- a/functions/src/webhooks/website/survey-login/index.ts +++ b/functions/src/webhooks/website/survey-login/index.ts @@ -1,13 +1,13 @@ import assert from 'assert'; import { onCall } from 'firebase-functions/v2/https'; import { FirestoreAdmin } from '../../../../../shared/src/firebase/admin/FirestoreAdmin'; -import { Recipient, RECIPIENT_FIRESTORE_PATH } from '../../../../../shared/src/types/Recipient'; +import { Recipient, RECIPIENT_FIRESTORE_PATH } from '../../../../../shared/src/types/recipient'; import { Survey, SURVEY_FIRETORE_PATH, SurveyCredentialRequest, SurveyCredentialResponse, -} from '../../../../../shared/src/types/Survey'; +} from '../../../../../shared/src/types/survey'; export default onCall>(async (request) => { const firestoreAdmin = new FirestoreAdmin(); diff --git a/package-lock.json b/package-lock.json index a704b67a5..884bf89da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10615,6 +10615,11 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@types/js-cookie": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz", + "integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==" + }, "node_modules/@types/json-schema": { "version": "7.0.14", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", @@ -14637,6 +14642,11 @@ "node": ">= 6" } }, + "node_modules/country-flag-icons": { + "version": "1.5.7", + "resolved": "https://registry.npmjs.org/country-flag-icons/-/country-flag-icons-1.5.7.tgz", + "integrity": "sha512-AdvXhMcmSp7nBSkpGfW4qR/luAdRUutJqya9PuwRbsBzuoknThfultbv7Ib6fWsHXC43Es/4QJ8gzQQdBNm75A==" + }, "node_modules/crc-32": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", @@ -23715,6 +23725,14 @@ "node": ">=10" } }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "engines": { + "node": ">=14" + } + }, "node_modules/js-search": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/js-search/-/js-search-2.0.1.tgz", @@ -36835,11 +36853,14 @@ "@socialincome/shared": "^1.0.0", "@socialincome/ui": "^1.0.0", "@tanstack/react-query": "^5.0.5", + "@types/js-cookie": "^3.0.6", "@vimeo/player": "^2.20.1", "accept-language-parser": "^1.5.0", "classnames": "^2.3.2", + "country-flag-icons": "^1.5.7", "firebase": "^9.23.0", "i18next": "^23.6.0", + "js-cookie": "^3.0.5", "lodash": "^4.17.21", "next": "14.0.0", "react": "18.2.0", diff --git a/shared/locales/de/common.json b/shared/locales/de/common.json index 4eee199f2..58e1b347d 100644 --- a/shared/locales/de/common.json +++ b/shared/locales/de/common.json @@ -1,4 +1,18 @@ { + "currency": "Währung", + "currencies": { + "CHF": "Schweizer Franken", + "USD": "US Dollar", + "EUR": "Euro", + "SLE": "Sierra-leonischer Leone" + }, + "region": "Region", + "regions": { + "int": "International", + "ch": "Schweiz", + "sl": "Sierra Leone" + }, + "language": "Sprache", "languages": { "de": "Deutsch", "en": "Englisch", diff --git a/shared/locales/en/common.json b/shared/locales/en/common.json index a41c9994e..4a9ed9a34 100644 --- a/shared/locales/en/common.json +++ b/shared/locales/en/common.json @@ -1,4 +1,18 @@ { + "currency": "Currency", + "currencies": { + "CHF": "Swiss Franc", + "USD": "US Dollar", + "EUR": "Euro", + "SLE": "Sierra Leonean Leone" + }, + "region": "Region", + "regions": { + "int": "International", + "ch": "Switzerland", + "sl": "Sierra Leone" + }, + "language": "Language", "languages": { "de": "German", "en": "English", diff --git a/shared/src/firebase/admin/FirestoreAdmin.ts b/shared/src/firebase/admin/FirestoreAdmin.ts index c8236c7a1..4d72a031d 100644 --- a/shared/src/firebase/admin/FirestoreAdmin.ts +++ b/shared/src/firebase/admin/FirestoreAdmin.ts @@ -10,7 +10,7 @@ import { Query, QueryDocumentSnapshot, } from 'firebase-admin/firestore'; -import { AdminUser } from '../../types/AdminUser'; +import { AdminUser } from '../../types/admin-user'; import { getOrInitializeFirebaseAdmin } from './app'; import Firestore = firestore.Firestore; diff --git a/shared/src/firebase/admin/utils.ts b/shared/src/firebase/admin/utils.ts index 2336ed15f..1707a5aa8 100644 --- a/shared/src/firebase/admin/utils.ts +++ b/shared/src/firebase/admin/utils.ts @@ -1,6 +1,6 @@ import { Timestamp as FirestoreAdminTimestamp } from '@google-cloud/firestore'; import { DateTime } from 'luxon'; -import { Timestamp } from '../../types/Timestamp'; +import { Timestamp } from '../../types/timestamp'; export function toFirebaseAdminTimestamp(dateTime: DateTime | Date): Timestamp { if (dateTime instanceof Date) { diff --git a/shared/src/stripe/StripeEventHandler.ts b/shared/src/stripe/StripeEventHandler.ts index 4350ac1df..b418cbeaf 100644 --- a/shared/src/stripe/StripeEventHandler.ts +++ b/shared/src/stripe/StripeEventHandler.ts @@ -8,8 +8,8 @@ import { ContributionSourceKey, StatusKey, StripeContribution, -} from '../types/Contribution'; -import { USER_FIRESTORE_PATH, User, UserStatusKey, splitName } from '../types/User'; +} from '../types/contribution'; +import { USER_FIRESTORE_PATH, User, UserStatusKey, splitName } from '../types/user'; export class StripeEventHandler { readonly stripe: Stripe; diff --git a/shared/src/stripe/StripeWebhookHandler.test.ts b/shared/src/stripe/StripeWebhookHandler.test.ts index 544337b14..9d118a1de 100644 --- a/shared/src/stripe/StripeWebhookHandler.test.ts +++ b/shared/src/stripe/StripeWebhookHandler.test.ts @@ -4,8 +4,8 @@ import Stripe from 'stripe'; import { FirestoreAdmin } from '../firebase/admin/FirestoreAdmin'; import { getOrInitializeFirebaseAdmin } from '../firebase/admin/app'; import { toFirebaseAdminTimestamp } from '../firebase/admin/utils'; -import { ContributionSourceKey, StatusKey, StripeContribution } from '../types/Contribution'; -import { User, UserStatusKey } from '../types/User'; +import { ContributionSourceKey, StatusKey, StripeContribution } from '../types/contribution'; +import { User, UserStatusKey } from '../types/user'; import { StripeEventHandler } from './StripeEventHandler'; describe('stripeWebhook', () => { diff --git a/shared/src/types/AdminUser.ts b/shared/src/types/admin-user.ts similarity index 100% rename from shared/src/types/AdminUser.ts rename to shared/src/types/admin-user.ts diff --git a/shared/src/types/BankBalance.ts b/shared/src/types/bank-balance.ts similarity index 100% rename from shared/src/types/BankBalance.ts rename to shared/src/types/bank-balance.ts diff --git a/shared/src/types/Contribution.ts b/shared/src/types/contribution.ts similarity index 95% rename from shared/src/types/Contribution.ts rename to shared/src/types/contribution.ts index fcbb828f1..b23ac8621 100644 --- a/shared/src/types/Contribution.ts +++ b/shared/src/types/contribution.ts @@ -1,4 +1,4 @@ -import { Timestamp } from './Timestamp'; +import { Timestamp } from './timestamp'; export const CONTRIBUTION_FIRESTORE_PATH = 'contributions'; diff --git a/shared/src/types/country.ts b/shared/src/types/country.ts new file mode 100644 index 000000000..d5e11de65 --- /dev/null +++ b/shared/src/types/country.ts @@ -0,0 +1,253 @@ +const COUNTRIES = { + AF: 'Afghanistan', + AX: 'Åland Islands', + AL: 'Albania', + DZ: 'Algeria', + AS: 'American Samoa', + AD: 'Andorra', + AO: 'Angola', + AI: 'Anguilla', + AQ: 'Antarctica', + AG: 'Antigua and Barbuda', + AR: 'Argentina', + AM: 'Armenia', + AW: 'Aruba', + AU: 'Australia', + AT: 'Austria', + AZ: 'Azerbaijan', + BS: 'Bahamas', + BH: 'Bahrain', + BD: 'Bangladesh', + BB: 'Barbados', + BY: 'Belarus', + BE: 'Belgium', + BZ: 'Belize', + BJ: 'Benin', + BM: 'Bermuda', + BT: 'Bhutan', + BO: 'Bolivia, Plurinational State of', + BQ: 'Bonaire, Sint Eustatius and Saba', + BA: 'Bosnia and Herzegovina', + BW: 'Botswana', + BV: 'Bouvet Island', + BR: 'Brazil', + IO: 'British Indian Ocean Territory', + BN: 'Brunei Darussalam', + BG: 'Bulgaria', + BF: 'Burkina Faso', + BI: 'Burundi', + KH: 'Cambodia', + CM: 'Cameroon', + CA: 'Canada', + CV: 'Cape Verde', + KY: 'Cayman Islands', + CF: 'Central African Republic', + TD: 'Chad', + CL: 'Chile', + CN: 'China', + CX: 'Christmas Island', + CC: 'Cocos (Keeling) Islands', + CO: 'Colombia', + KM: 'Comoros', + CG: 'Congo', + CD: 'Congo, the Democratic Republic of the', + CK: 'Cook Islands', + CR: 'Costa Rica', + CI: "Côte d'Ivoire", + HR: 'Croatia', + CU: 'Cuba', + CW: 'Curaçao', + CY: 'Cyprus', + CZ: 'Czech Republic', + DK: 'Denmark', + DJ: 'Djibouti', + DM: 'Dominica', + DO: 'Dominican Republic', + EC: 'Ecuador', + EG: 'Egypt', + SV: 'El Salvador', + GQ: 'Equatorial Guinea', + ER: 'Eritrea', + EE: 'Estonia', + ET: 'Ethiopia', + FK: 'Falkland Islands (Malvinas)', + FO: 'Faroe Islands', + FJ: 'Fiji', + FI: 'Finland', + FR: 'France', + GF: 'French Guiana', + PF: 'French Polynesia', + TF: 'French Southern Territories', + GA: 'Gabon', + GM: 'Gambia', + GE: 'Georgia', + DE: 'Germany', + GH: 'Ghana', + GI: 'Gibraltar', + GR: 'Greece', + GL: 'Greenland', + GD: 'Grenada', + GP: 'Guadeloupe', + GU: 'Guam', + GT: 'Guatemala', + GG: 'Guernsey', + GN: 'Guinea', + GW: 'Guinea-Bissau', + GY: 'Guyana', + HT: 'Haiti', + HM: 'Heard Island and McDonald Islands', + VA: 'Holy See (Vatican City State)', + HN: 'Honduras', + HK: 'Hong Kong', + HU: 'Hungary', + IS: 'Iceland', + IN: 'India', + ID: 'Indonesia', + IR: 'Iran, Islamic Republic of', + IQ: 'Iraq', + IE: 'Ireland', + IM: 'Isle of Man', + IL: 'Israel', + IT: 'Italy', + JM: 'Jamaica', + JP: 'Japan', + JE: 'Jersey', + JO: 'Jordan', + KZ: 'Kazakhstan', + KE: 'Kenya', + KI: 'Kiribati', + KP: "Korea, Democratic People's Republic of", + KR: 'Korea, Republic of', + KW: 'Kuwait', + KG: 'Kyrgyzstan', + LA: "Lao People's Democratic Republic", + LV: 'Latvia', + LB: 'Lebanon', + LS: 'Lesotho', + LR: 'Liberia', + LY: 'Libya', + LI: 'Liechtenstein', + LT: 'Lithuania', + LU: 'Luxembourg', + MO: 'Macao', + MK: 'Macedonia, the Former Yugoslav Republic of', + MG: 'Madagascar', + MW: 'Malawi', + MY: 'Malaysia', + MV: 'Maldives', + ML: 'Mali', + MT: 'Malta', + MH: 'Marshall Islands', + MQ: 'Martinique', + MR: 'Mauritania', + MU: 'Mauritius', + YT: 'Mayotte', + MX: 'Mexico', + FM: 'Micronesia, Federated States of', + MD: 'Moldova, Republic of', + MC: 'Monaco', + MN: 'Mongolia', + ME: 'Montenegro', + MS: 'Montserrat', + MA: 'Morocco', + MZ: 'Mozambique', + MM: 'Myanmar', + NA: 'Namibia', + NR: 'Nauru', + NP: 'Nepal', + NL: 'Netherlands', + NC: 'New Caledonia', + NZ: 'New Zealand', + NI: 'Nicaragua', + NE: 'Niger', + NG: 'Nigeria', + NU: 'Niue', + NF: 'Norfolk Island', + MP: 'Northern Mariana Islands', + NO: 'Norway', + OM: 'Oman', + PK: 'Pakistan', + PW: 'Palau', + PS: 'Palestine, State of', + PA: 'Panama', + PG: 'Papua New Guinea', + PY: 'Paraguay', + PE: 'Peru', + PH: 'Philippines', + PN: 'Pitcairn', + PL: 'Poland', + PT: 'Portugal', + PR: 'Puerto Rico', + QA: 'Qatar', + RE: 'Réunion', + RO: 'Romania', + RU: 'Russian Federation', + RW: 'Rwanda', + BL: 'Saint Barthélemy', + SH: 'Saint Helena, Ascension and Tristan da Cunha', + KN: 'Saint Kitts and Nevis', + LC: 'Saint Lucia', + MF: 'Saint Martin (French part)', + PM: 'Saint Pierre and Miquelon', + VC: 'Saint Vincent and the Grenadines', + WS: 'Samoa', + SM: 'San Marino', + ST: 'Sao Tome and Principe', + SA: 'Saudi Arabia', + SN: 'Senegal', + RS: 'Serbia', + SC: 'Seychelles', + SL: 'Sierra Leone', + SG: 'Singapore', + SX: 'Sint Maarten (Dutch part)', + SK: 'Slovakia', + SI: 'Slovenia', + SB: 'Solomon Islands', + SO: 'Somalia', + ZA: 'South Africa', + GS: 'South Georgia and the South Sandwich Islands', + SS: 'South Sudan', + ES: 'Spain', + LK: 'Sri Lanka', + SD: 'Sudan', + SR: 'Suriname', + SJ: 'Svalbard and Jan Mayen', + SZ: 'Swaziland', + SE: 'Sweden', + CH: 'Switzerland', + SY: 'Syrian Arab Republic', + TW: 'Taiwan, Province of China', + TJ: 'Tajikistan', + TZ: 'Tanzania, United Republic of', + TH: 'Thailand', + TL: 'Timor-Leste', + TG: 'Togo', + TK: 'Tokelau', + TO: 'Tonga', + TT: 'Trinidad and Tobago', + TN: 'Tunisia', + TR: 'Turkey', + TM: 'Turkmenistan', + TC: 'Turks and Caicos Islands', + TV: 'Tuvalu', + UG: 'Uganda', + UA: 'Ukraine', + AE: 'United Arab Emirates', + GB: 'United Kingdom', + US: 'United States', + UM: 'United States Minor Outlying Islands', + UY: 'Uruguay', + UZ: 'Uzbekistan', + VU: 'Vanuatu', + VE: 'Venezuela, Bolivarian Republic of', + VN: 'Viet Nam', + VG: 'Virgin Islands, British', + VI: 'Virgin Islands, U.S.', + WF: 'Wallis and Futuna', + EH: 'Western Sahara', + YE: 'Yemen', + ZM: 'Zambia', + ZW: 'Zimbabwe', +}; + +export type CountryCode = keyof typeof COUNTRIES; diff --git a/shared/src/utils/currency.ts b/shared/src/types/currency.ts similarity index 59% rename from shared/src/utils/currency.ts rename to shared/src/types/currency.ts index 81636df03..9a3cf2ea5 100644 --- a/shared/src/utils/currency.ts +++ b/shared/src/types/currency.ts @@ -1,13 +1,8 @@ -// best guess mapping from two-letter country code to supported currency -export const bestGuessCurrency = (country: string | undefined): string => { - if (!country) return fallbackCurrency; - const uppercaseCountry = country.toUpperCase(); - const currency = countryToCurrency.get(uppercaseCountry); - return currency || fallbackCurrency; -}; +export const CURRENCIES = ['CHF', 'EUR', 'USD', 'SLE'] as const; +export type Currency = (typeof CURRENCIES)[number]; -const fallbackCurrency = 'USD'; -const countryToCurrency = new Map([ +export const FALLBACK_CURRENCY: Currency = 'USD'; +const countryToCurrency = new Map([ ['AD', 'EUR'], ['AL', 'EUR'], ['AM', 'EUR'], @@ -46,3 +41,15 @@ const countryToCurrency = new Map([ ['PO', 'EUR'], ['PT', 'EUR'], ]); + +export const bestGuessCurrency = (country: string | undefined): Currency => { + // best guess mapping from two-letter country code to supported currency + if (!country) return FALLBACK_CURRENCY; + const uppercaseCountry = country.toUpperCase(); + const currency = countryToCurrency.get(uppercaseCountry); + return currency || FALLBACK_CURRENCY; +}; + +export const isValidCurrency = (currency: string | undefined): currency is Currency => { + return CURRENCIES.includes(currency as Currency); +}; diff --git a/shared/src/types/DonationCertificate.ts b/shared/src/types/donation-certificate.ts similarity index 100% rename from shared/src/types/DonationCertificate.ts rename to shared/src/types/donation-certificate.ts diff --git a/shared/src/types/ExchangeRates.ts b/shared/src/types/exchange-rates.ts similarity index 100% rename from shared/src/types/ExchangeRates.ts rename to shared/src/types/exchange-rates.ts diff --git a/shared/src/types/Language.ts b/shared/src/types/language.ts similarity index 100% rename from shared/src/types/Language.ts rename to shared/src/types/language.ts diff --git a/shared/src/types/Message.ts b/shared/src/types/message.ts similarity index 100% rename from shared/src/types/Message.ts rename to shared/src/types/message.ts diff --git a/shared/src/types/OperationalExpense.ts b/shared/src/types/operational-expense.ts similarity index 100% rename from shared/src/types/OperationalExpense.ts rename to shared/src/types/operational-expense.ts diff --git a/shared/src/types/PartnerOrganisation.ts b/shared/src/types/partner-organisation.ts similarity index 100% rename from shared/src/types/PartnerOrganisation.ts rename to shared/src/types/partner-organisation.ts diff --git a/shared/src/types/Payment.ts b/shared/src/types/payment.ts similarity index 91% rename from shared/src/types/Payment.ts rename to shared/src/types/payment.ts index 13b6071ce..aa66e1112 100644 --- a/shared/src/types/Payment.ts +++ b/shared/src/types/payment.ts @@ -1,5 +1,5 @@ -import { Timestamp } from '@socialincome/shared/src/types/Timestamp'; import { EntityReference } from 'firecms'; +import { Timestamp } from './timestamp'; export const PAYMENT_FIRESTORE_PATH = 'payments'; diff --git a/shared/src/types/Recipient.test.ts b/shared/src/types/recipient.test.ts similarity index 92% rename from shared/src/types/Recipient.test.ts rename to shared/src/types/recipient.test.ts index 52f08bf53..dd65d292d 100644 --- a/shared/src/types/Recipient.test.ts +++ b/shared/src/types/recipient.test.ts @@ -1,7 +1,7 @@ import { describe, test } from '@jest/globals'; import { DateTime } from 'luxon'; -import { calcFinalPaymentDate, calcPaymentsLeft } from './Recipient'; +import { calcFinalPaymentDate, calcPaymentsLeft } from './recipient'; describe('Recipient', () => { test('calc last payment', () => { diff --git a/shared/src/types/Recipient.ts b/shared/src/types/recipient.ts similarity index 97% rename from shared/src/types/Recipient.ts rename to shared/src/types/recipient.ts index 3ad7cb5cc..bdd2175b4 100644 --- a/shared/src/types/Recipient.ts +++ b/shared/src/types/recipient.ts @@ -1,5 +1,5 @@ import { DateTime } from 'luxon'; -import { Timestamp } from './Timestamp'; +import { Timestamp } from './timestamp'; export const RECIPIENT_FIRESTORE_PATH = 'recipients'; diff --git a/shared/src/types/Survey.ts b/shared/src/types/survey.ts similarity index 97% rename from shared/src/types/Survey.ts rename to shared/src/types/survey.ts index 0e1c3e4c0..725d6a410 100644 --- a/shared/src/types/Survey.ts +++ b/shared/src/types/survey.ts @@ -1,5 +1,5 @@ -import { RecipientMainLanguage } from './Recipient'; -import { Timestamp } from './Timestamp'; +import { RecipientMainLanguage } from './recipient'; +import { Timestamp } from './timestamp'; export const SURVEY_FIRETORE_PATH = 'surveys'; diff --git a/shared/src/types/Timestamp.ts b/shared/src/types/timestamp.ts similarity index 100% rename from shared/src/types/Timestamp.ts rename to shared/src/types/timestamp.ts diff --git a/shared/src/types/User.ts b/shared/src/types/user.ts similarity index 97% rename from shared/src/types/User.ts rename to shared/src/types/user.ts index 5936f695f..85b04c315 100644 --- a/shared/src/types/User.ts +++ b/shared/src/types/user.ts @@ -1,6 +1,6 @@ import { EntityReference } from 'firecms'; import { capitalizeStringIfUppercase } from '../utils/strings'; -import { LanguageCode } from './Language'; +import { LanguageCode } from './language'; export const USER_FIRESTORE_PATH = 'users'; diff --git a/shared/src/utils/date.ts b/shared/src/utils/date.ts index 0278e4614..a6b219f59 100644 --- a/shared/src/utils/date.ts +++ b/shared/src/utils/date.ts @@ -1,5 +1,5 @@ import { DateTime } from 'luxon'; -import { Timestamp } from '../types/Timestamp'; +import { Timestamp } from '../types/timestamp'; export function getMonthId(year: number, month: number) { return year + '-' + (month + '').padStart(2, '0'); diff --git a/shared/src/utils/exchangeRates.ts b/shared/src/utils/exchangeRates.ts index 18c5c04a8..555293a96 100644 --- a/shared/src/utils/exchangeRates.ts +++ b/shared/src/utils/exchangeRates.ts @@ -1,5 +1,5 @@ import { FirestoreAdmin } from '../firebase/admin/FirestoreAdmin'; -import { EXCHANGE_RATES_PATH, ExchangeRatesEntry } from '../types/ExchangeRates'; +import { EXCHANGE_RATES_PATH, ExchangeRatesEntry } from '../types/exchange-rates'; export const getLatestExchangeRate = async (firestoreAdmin: FirestoreAdmin, currency: string): Promise => { if (currency === 'CHF') return 1.0; diff --git a/shared/src/utils/i18n.ts b/shared/src/utils/i18n.ts index 54a626068..36a0eb7eb 100644 --- a/shared/src/utils/i18n.ts +++ b/shared/src/utils/i18n.ts @@ -1,6 +1,6 @@ import i18next, { i18n } from 'i18next'; import resourcesToBackend from 'i18next-resources-to-backend'; -import { LanguageCode } from '../types/Language'; +import { LanguageCode } from '../types/language'; export const FALLBACK_LANGUAGE = 'en'; diff --git a/shared/src/utils/stats/ContributionStatsCalculator.test.ts b/shared/src/utils/stats/ContributionStatsCalculator.test.ts index 9f0615f91..d729af1c7 100644 --- a/shared/src/utils/stats/ContributionStatsCalculator.test.ts +++ b/shared/src/utils/stats/ContributionStatsCalculator.test.ts @@ -4,8 +4,8 @@ import { DateTime } from 'luxon'; import { FirestoreAdmin } from '../../firebase/admin/FirestoreAdmin'; import { getOrInitializeFirebaseAdmin } from '../../firebase/admin/app'; import { toFirebaseAdminTimestamp } from '../../firebase/admin/utils'; -import { CONTRIBUTION_FIRESTORE_PATH, ContributionSourceKey, StatusKey } from '../../types/Contribution'; -import { USER_FIRESTORE_PATH, User, UserStatusKey } from '../../types/User'; +import { CONTRIBUTION_FIRESTORE_PATH, ContributionSourceKey, StatusKey } from '../../types/contribution'; +import { USER_FIRESTORE_PATH, User, UserStatusKey } from '../../types/user'; import { ContributionStatsCalculator } from './ContributionStatsCalculator'; const projectId = 'contribution-stats-calculator-test'; diff --git a/shared/src/utils/stats/ContributionStatsCalculator.ts b/shared/src/utils/stats/ContributionStatsCalculator.ts index 20da771de..fbce72d5b 100644 --- a/shared/src/utils/stats/ContributionStatsCalculator.ts +++ b/shared/src/utils/stats/ContributionStatsCalculator.ts @@ -1,8 +1,8 @@ import _ from 'lodash'; import { DateTime } from 'luxon'; import { FirestoreAdmin } from '../../firebase/admin/FirestoreAdmin'; -import { Contribution, CONTRIBUTION_FIRESTORE_PATH, StatusKey } from '../../types/Contribution'; -import { User, USER_FIRESTORE_PATH } from '../../types/User'; +import { Contribution, CONTRIBUTION_FIRESTORE_PATH, StatusKey } from '../../types/contribution'; +import { User, USER_FIRESTORE_PATH } from '../../types/user'; import { getLatestExchangeRate } from '../exchangeRates'; import { cumulativeSum, StatsEntry } from './utils'; diff --git a/shared/src/utils/stats/PaymentStatsCalculator.ts b/shared/src/utils/stats/PaymentStatsCalculator.ts index ee43db559..adc12c30c 100644 --- a/shared/src/utils/stats/PaymentStatsCalculator.ts +++ b/shared/src/utils/stats/PaymentStatsCalculator.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; import { FirestoreAdmin } from '../../firebase/admin/FirestoreAdmin'; -import { Payment, PAYMENT_FIRESTORE_PATH } from '../../types/Payment'; +import { Payment, PAYMENT_FIRESTORE_PATH } from '../../types/payment'; import { toDateTime } from '../date'; import { getLatestExchangeRate } from '../exchangeRates'; import { cumulativeSum, groupByAndSort, StatsEntry } from './utils'; diff --git a/shared/src/utils/templates.ts b/shared/src/utils/templates.ts index 984ef5375..f144fcb1e 100644 --- a/shared/src/utils/templates.ts +++ b/shared/src/utils/templates.ts @@ -6,7 +6,7 @@ import i18next from 'i18next'; import resourcesToBackend from 'i18next-resources-to-backend'; import mjml2html from 'mjml'; import path from 'path'; -import { LanguageCode } from '../types/Language'; +import { LanguageCode } from '../types/language'; import { FALLBACK_LANGUAGE } from './i18n'; export interface RenderTemplateProps { diff --git a/shared/tests/firestore.rules.test.ts b/shared/tests/firestore.rules.test.ts index a05d7dae2..0495e1a86 100644 --- a/shared/tests/firestore.rules.test.ts +++ b/shared/tests/firestore.rules.test.ts @@ -9,8 +9,8 @@ import firebase from 'firebase/compat/app'; import { collection, doc, getDoc, getDocs, query, setDoc } from 'firebase/firestore'; import * as fs from 'fs'; import * as path from 'path'; -import { AdminUser } from '../src/types/AdminUser'; -import { USER_FIRESTORE_PATH } from '../src/types/User'; +import { AdminUser } from '../src/types/admin-user'; +import { USER_FIRESTORE_PATH } from '../src/types/user'; let testEnvironment: RulesTestEnvironment; let globalAdminStore: firebase.firestore.Firestore; diff --git a/ui/src/components/input.tsx b/ui/src/components/input.tsx index a43c311dc..436f8909a 100644 --- a/ui/src/components/input.tsx +++ b/ui/src/components/input.tsx @@ -9,7 +9,7 @@ const Input = React.forwardRef(({ className, type, { - const pathSegments = window.location.pathname.split('/'); - pathSegments[1] = lang; - const current = new URLSearchParams(Array.from(searchParams.entries())); - router.push(pathSegments.join('/') + '?' + current.toString()); - }; - - return ( - - - - - - - {languages.map((language, index) => ( - onLanguageChange(language)}> - {translator?.t(`languages.${language}`)} - - ))} - - - - ); -} - -export function LanguageSwitcher(props: LanguageSwitcherProps) { - return ( - - - - ); -} diff --git a/website/src/app/[lang]/[country]/(survey-tool)/survey/[recipient]/[survey]/layout.tsx b/website/src/app/[lang]/[country]/(survey-tool)/survey/[recipient]/[survey]/layout.tsx deleted file mode 100644 index c79a3fb28..000000000 --- a/website/src/app/[lang]/[country]/(survey-tool)/survey/[recipient]/[survey]/layout.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { DefaultParams } from '@/app/[lang]/[country]'; -import { SILogo } from '@/components/logos/si-logo'; -import { PropsWithChildren } from 'react'; -import { LanguageSwitcher } from './language-switcher'; - -export interface SurveyPageProps { - params: { - recipient: string; - survey: string; - } & DefaultParams; -} - -export default function Layout({ children, params }: PropsWithChildren) { - return ( -
-
-
-
- -
-
- -
-
-
{children}
-
- ); -} diff --git a/website/src/app/[lang]/[country]/(website)/(home)/section-1-input.tsx b/website/src/app/[lang]/[country]/(website)/(home)/section-1-input.tsx deleted file mode 100644 index 07c26a58f..000000000 --- a/website/src/app/[lang]/[country]/(website)/(home)/section-1-input.tsx +++ /dev/null @@ -1,49 +0,0 @@ -'use client'; - -import { zodResolver } from '@hookform/resolvers/zod'; -import { Button, Form, FormControl, FormField, FormItem, Input } from '@socialincome/ui'; -import { useRouter } from 'next/navigation'; -import { useForm } from 'react-hook-form'; -import * as z from 'zod'; - -interface Section1InputProps { - text: string; -} - -// TODO: i18n -export default function Section1Input({ text }: Section1InputProps) { - const router = useRouter(); - - const formSchema = z.object({ - amount: z.coerce.number().min(1).optional(), - }); - - type FormSchema = z.infer; - const form = useForm({ - resolver: zodResolver(formSchema), - defaultValues: { amount: '' as any }, - }); - - const onSubmit = (values: FormSchema) => { - router.push(`/donate/individual?amount=${(Number(values.amount) / 100).toFixed(2)}`); - }; - - return ( -
- - ( - - - - - - )} - /> - - - - ); -} diff --git a/website/src/app/[lang]/[country]/(website)/about-us/page.tsx b/website/src/app/[lang]/[country]/(website)/about-us/page.tsx deleted file mode 100644 index 656f189ca..000000000 --- a/website/src/app/[lang]/[country]/(website)/about-us/page.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { DefaultPageProps } from '@/app/[lang]/[country]'; -import Section1 from './section-1'; - -export default async function Page(props: DefaultPageProps) { - return ( - <> - - - ); -} diff --git a/website/src/app/[lang]/[country]/(website)/layout.tsx b/website/src/app/[lang]/[country]/(website)/layout.tsx deleted file mode 100644 index be65ee734..000000000 --- a/website/src/app/[lang]/[country]/(website)/layout.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { DefaultLayoutProps } from '@/app/[lang]/[country]'; -import Footer from '@/components/footer/footer'; -import Navbar from '@/components/navbar/navbar'; -import { countries, supportedWebsiteLanguages, websiteLanguages } from '@/i18n'; -import { PropsWithChildren } from 'react'; - -export const generateStaticParams = () => - countries.flatMap((country) => websiteLanguages.map((lang) => ({ lang, country }))); - -export default function Layout({ children, params }: PropsWithChildren) { - return ( -
- -
{children}
-
-
- ); -} diff --git a/website/src/app/[lang]/[country]/(survey-tool)/survey/[recipient]/[survey]/common.ts b/website/src/app/[lang]/[region]/(survey-tool)/survey/[recipient]/[survey]/common.ts similarity index 100% rename from website/src/app/[lang]/[country]/(survey-tool)/survey/[recipient]/[survey]/common.ts rename to website/src/app/[lang]/[region]/(survey-tool)/survey/[recipient]/[survey]/common.ts diff --git a/website/src/app/[lang]/[region]/(survey-tool)/survey/[recipient]/[survey]/layout.tsx b/website/src/app/[lang]/[region]/(survey-tool)/survey/[recipient]/[survey]/layout.tsx new file mode 100644 index 000000000..9108b04dd --- /dev/null +++ b/website/src/app/[lang]/[region]/(survey-tool)/survey/[recipient]/[survey]/layout.tsx @@ -0,0 +1,54 @@ +import { CURRENCY_COOKIE, DefaultParams } from '@/app/[lang]/[region]'; +import { I18nDialog } from '@/components/i18n-dialog'; +import { SILogo } from '@/components/logos/si-logo'; +import { GlobeEuropeAfricaIcon, LanguageIcon } from '@heroicons/react/24/solid'; +import { Translator } from '@socialincome/shared/src/utils/i18n'; +import { BaseContainer, Button } from '@socialincome/ui'; +import { cookies } from 'next/headers'; +import { PropsWithChildren } from 'react'; + +export interface SurveyPageProps { + params: { + recipient: string; + survey: string; + } & DefaultParams; +} + +export default async function Layout({ children, params: { lang, region } }: PropsWithChildren) { + const translator = await Translator.getInstance({ + language: lang, + namespaces: ['common', 'website-common', 'website-me'], + }); + + return ( +
+ +
{children}
+
+ ); +} diff --git a/website/src/app/[lang]/[country]/(survey-tool)/survey/[recipient]/[survey]/page.tsx b/website/src/app/[lang]/[region]/(survey-tool)/survey/[recipient]/[survey]/page.tsx similarity index 94% rename from website/src/app/[lang]/[country]/(survey-tool)/survey/[recipient]/[survey]/page.tsx rename to website/src/app/[lang]/[region]/(survey-tool)/survey/[recipient]/[survey]/page.tsx index 0e3f8847f..06c22cc54 100644 --- a/website/src/app/[lang]/[country]/(survey-tool)/survey/[recipient]/[survey]/page.tsx +++ b/website/src/app/[lang]/[region]/(survey-tool)/survey/[recipient]/[survey]/page.tsx @@ -1,6 +1,6 @@ 'use client'; -import { Survey, SurveyLanguage } from '@/app/[lang]/[country]/(survey-tool)/survey/[recipient]/[survey]/survey'; +import { Survey, SurveyLanguage } from '@/app/[lang]/[region]/(survey-tool)/survey/[recipient]/[survey]/survey'; import { Button, Input } from '@socialincome/ui'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { User, signInWithEmailAndPassword } from 'firebase/auth'; diff --git a/website/src/app/[lang]/[country]/(survey-tool)/survey/[recipient]/[survey]/questionnaires.ts b/website/src/app/[lang]/[region]/(survey-tool)/survey/[recipient]/[survey]/questionnaires.ts similarity index 99% rename from website/src/app/[lang]/[country]/(survey-tool)/survey/[recipient]/[survey]/questionnaires.ts rename to website/src/app/[lang]/[region]/(survey-tool)/survey/[recipient]/[survey]/questionnaires.ts index fbfde829c..3b58aa6eb 100644 --- a/website/src/app/[lang]/[country]/(survey-tool)/survey/[recipient]/[survey]/questionnaires.ts +++ b/website/src/app/[lang]/[region]/(survey-tool)/survey/[recipient]/[survey]/questionnaires.ts @@ -1,4 +1,4 @@ -import { SurveyQuestionnaire } from '@socialincome/shared/src/types/Survey'; +import { SurveyQuestionnaire } from '@socialincome/shared/src/types/survey'; import { TranslateFunction } from '@socialincome/shared/src/utils/i18n'; import { achievementsAchievedPage, diff --git a/website/src/app/[lang]/[country]/(survey-tool)/survey/[recipient]/[survey]/questions.ts b/website/src/app/[lang]/[region]/(survey-tool)/survey/[recipient]/[survey]/questions.ts similarity index 100% rename from website/src/app/[lang]/[country]/(survey-tool)/survey/[recipient]/[survey]/questions.ts rename to website/src/app/[lang]/[region]/(survey-tool)/survey/[recipient]/[survey]/questions.ts diff --git a/website/src/app/[lang]/[country]/(survey-tool)/survey/[recipient]/[survey]/survey.css b/website/src/app/[lang]/[region]/(survey-tool)/survey/[recipient]/[survey]/survey.css similarity index 100% rename from website/src/app/[lang]/[country]/(survey-tool)/survey/[recipient]/[survey]/survey.css rename to website/src/app/[lang]/[region]/(survey-tool)/survey/[recipient]/[survey]/survey.css diff --git a/website/src/app/[lang]/[country]/(survey-tool)/survey/[recipient]/[survey]/survey.tsx b/website/src/app/[lang]/[region]/(survey-tool)/survey/[recipient]/[survey]/survey.tsx similarity index 97% rename from website/src/app/[lang]/[country]/(survey-tool)/survey/[recipient]/[survey]/survey.tsx rename to website/src/app/[lang]/[region]/(survey-tool)/survey/[recipient]/[survey]/survey.tsx index 182424016..ac4df1415 100644 --- a/website/src/app/[lang]/[country]/(survey-tool)/survey/[recipient]/[survey]/survey.tsx +++ b/website/src/app/[lang]/[region]/(survey-tool)/survey/[recipient]/[survey]/survey.tsx @@ -1,7 +1,7 @@ import { useTranslator } from '@/hooks/useTranslator'; import { WebsiteLanguage } from '@/i18n'; -import { RECIPIENT_FIRESTORE_PATH } from '@socialincome/shared/src/types/Recipient'; -import { SURVEY_FIRETORE_PATH, Survey as SurveyModel, SurveyStatus } from '@socialincome/shared/src/types/Survey'; +import { RECIPIENT_FIRESTORE_PATH } from '@socialincome/shared/src/types/recipient'; +import { SURVEY_FIRETORE_PATH, Survey as SurveyModel, SurveyStatus } from '@socialincome/shared/src/types/survey'; import { useQuery } from '@tanstack/react-query'; import { doc, getDoc, updateDoc } from 'firebase/firestore'; import { useCallback } from 'react'; diff --git a/website/src/app/[lang]/[country]/(website)/(home)/(assets)/deza.svg b/website/src/app/[lang]/[region]/(website)/(home)/(assets)/deza.svg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/(home)/(assets)/deza.svg rename to website/src/app/[lang]/[region]/(website)/(home)/(assets)/deza.svg diff --git a/website/src/app/[lang]/[country]/(website)/(home)/(assets)/house.gif b/website/src/app/[lang]/[region]/(website)/(home)/(assets)/house.gif similarity index 100% rename from website/src/app/[lang]/[country]/(website)/(home)/(assets)/house.gif rename to website/src/app/[lang]/[region]/(website)/(home)/(assets)/house.gif diff --git a/website/src/app/[lang]/[country]/(website)/(home)/(assets)/innosuisse.svg b/website/src/app/[lang]/[region]/(website)/(home)/(assets)/innosuisse.svg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/(home)/(assets)/innosuisse.svg rename to website/src/app/[lang]/[region]/(website)/(home)/(assets)/innosuisse.svg diff --git a/website/src/app/[lang]/[country]/(website)/(home)/(assets)/lia.svg b/website/src/app/[lang]/[region]/(website)/(home)/(assets)/lia.svg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/(home)/(assets)/lia.svg rename to website/src/app/[lang]/[region]/(website)/(home)/(assets)/lia.svg diff --git a/website/src/app/[lang]/[country]/(website)/(home)/(assets)/sdg-logo.svg b/website/src/app/[lang]/[region]/(website)/(home)/(assets)/sdg-logo.svg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/(home)/(assets)/sdg-logo.svg rename to website/src/app/[lang]/[region]/(website)/(home)/(assets)/sdg-logo.svg diff --git a/website/src/app/[lang]/[country]/(website)/(home)/(assets)/whatsapp.svg b/website/src/app/[lang]/[region]/(website)/(home)/(assets)/whatsapp.svg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/(home)/(assets)/whatsapp.svg rename to website/src/app/[lang]/[region]/(website)/(home)/(assets)/whatsapp.svg diff --git a/website/src/app/[lang]/[country]/(website)/(home)/page.tsx b/website/src/app/[lang]/[region]/(website)/(home)/page.tsx similarity index 89% rename from website/src/app/[lang]/[country]/(website)/(home)/page.tsx rename to website/src/app/[lang]/[region]/(website)/(home)/page.tsx index 02193c817..77c076e12 100644 --- a/website/src/app/[lang]/[country]/(website)/(home)/page.tsx +++ b/website/src/app/[lang]/[region]/(website)/(home)/page.tsx @@ -1,4 +1,4 @@ -import { DefaultPageProps } from '@/app/[lang]/[country]'; +import { DefaultPageProps } from '@/app/[lang]/[region]'; import { Translator } from '@socialincome/shared/src/utils/i18n'; import Section1 from './section-1'; import Section2 from './section-2'; @@ -23,7 +23,7 @@ export default async function Page(props: DefaultPageProps) { videoButton: translator.t('section-2.video-button'), }} /> - {props.params.country === 'ch' && } + {props.params.region === 'ch' && } diff --git a/website/src/app/[lang]/[region]/(website)/(home)/section-1-form.tsx b/website/src/app/[lang]/[region]/(website)/(home)/section-1-form.tsx new file mode 100644 index 000000000..3b00d7798 --- /dev/null +++ b/website/src/app/[lang]/[region]/(website)/(home)/section-1-form.tsx @@ -0,0 +1,66 @@ +'use client'; + +import { CurrencySelector } from '@/components/ui/currency-selector'; +import { websiteCurrencies } from '@/i18n'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { Button, Form, FormControl, FormField, FormItem, Input, Typography } from '@socialincome/ui'; +import { useRouter } from 'next/navigation'; +import { useForm } from 'react-hook-form'; +import * as z from 'zod'; + +interface Section1InputProps { + translations: { + text: string; + // amount: string; + submit: string; + currency: string; + }; +} + +// TODO: i18n +export default function Section1Form({ translations }: Section1InputProps) { + const router = useRouter(); + + const formSchema = z.object({ + amount: z.coerce.number().min(1).optional(), + }); + + type FormSchema = z.infer; + const form = useForm({ + resolver: zodResolver(formSchema), + defaultValues: { amount: '' as any }, + }); + + const onSubmit = (values: FormSchema) => { + router.push(`/donate/individual?amount=${(Number(values.amount) / 100).toFixed(2)}`); + }; + + return ( +
+ {translations.text} +
+ +
+ ( + + + + + + )} + /> + +
+ +
+ +
+ ); +} diff --git a/website/src/app/[lang]/[country]/(website)/(home)/section-1.tsx b/website/src/app/[lang]/[region]/(website)/(home)/section-1.tsx similarity index 52% rename from website/src/app/[lang]/[country]/(website)/(home)/section-1.tsx rename to website/src/app/[lang]/[region]/(website)/(home)/section-1.tsx index 334a5d128..4b9762961 100644 --- a/website/src/app/[lang]/[country]/(website)/(home)/section-1.tsx +++ b/website/src/app/[lang]/[region]/(website)/(home)/section-1.tsx @@ -1,17 +1,20 @@ -import { DefaultPageProps } from '@/app/[lang]/[country]'; +import { DefaultPageProps } from '@/app/[lang]/[region]'; import { Translator } from '@socialincome/shared/src/utils/i18n'; import { BaseContainer, Typography } from '@socialincome/ui'; -import Section1Input from './section-1-input'; +import Section1Form from './section-1-form'; export default async function Section1({ params }: DefaultPageProps) { const translator = await Translator.getInstance({ language: params.lang, - namespaces: ['website-home'], + namespaces: ['website-home', 'common'], }); return ( - -
+ +
{translator.t('section-1.title-1')} @@ -20,9 +23,14 @@ export default async function Section1({ params }: DefaultPageProps) { {translator.t('section-1.title-3')}
-
- {translator.t('section-1.income-text')} - +
+
); diff --git a/website/src/app/[lang]/[country]/(website)/(home)/section-2.tsx b/website/src/app/[lang]/[region]/(website)/(home)/section-2.tsx similarity index 100% rename from website/src/app/[lang]/[country]/(website)/(home)/section-2.tsx rename to website/src/app/[lang]/[region]/(website)/(home)/section-2.tsx diff --git a/website/src/app/[lang]/[country]/(website)/(home)/section-3.tsx b/website/src/app/[lang]/[region]/(website)/(home)/section-3.tsx similarity index 73% rename from website/src/app/[lang]/[country]/(website)/(home)/section-3.tsx rename to website/src/app/[lang]/[region]/(website)/(home)/section-3.tsx index a71cd4268..f2a1c89b4 100644 --- a/website/src/app/[lang]/[country]/(website)/(home)/section-3.tsx +++ b/website/src/app/[lang]/[region]/(website)/(home)/section-3.tsx @@ -1,7 +1,6 @@ -import { DefaultPageProps } from '@/app/[lang]/[country]'; import { BaseContainer, Typography } from '@socialincome/ui'; -export default function Section3({ params }: DefaultPageProps) { +export default function Section3() { return (
diff --git a/website/src/app/[lang]/[country]/(website)/(home)/section-4.tsx b/website/src/app/[lang]/[region]/(website)/(home)/section-4.tsx similarity index 95% rename from website/src/app/[lang]/[country]/(website)/(home)/section-4.tsx rename to website/src/app/[lang]/[region]/(website)/(home)/section-4.tsx index 485801adc..3333d270b 100644 --- a/website/src/app/[lang]/[country]/(website)/(home)/section-4.tsx +++ b/website/src/app/[lang]/[region]/(website)/(home)/section-4.tsx @@ -1,4 +1,4 @@ -import { DefaultPageProps } from '@/app/[lang]/[country]'; +import { DefaultPageProps } from '@/app/[lang]/[region]'; import { Translator } from '@socialincome/shared/src/utils/i18n'; import { BaseContainer, Typography } from '@socialincome/ui'; diff --git a/website/src/app/[lang]/[country]/(website)/(home)/section-5-card.tsx b/website/src/app/[lang]/[region]/(website)/(home)/section-5-card.tsx similarity index 100% rename from website/src/app/[lang]/[country]/(website)/(home)/section-5-card.tsx rename to website/src/app/[lang]/[region]/(website)/(home)/section-5-card.tsx diff --git a/website/src/app/[lang]/[country]/(website)/(home)/section-5.tsx b/website/src/app/[lang]/[region]/(website)/(home)/section-5.tsx similarity index 97% rename from website/src/app/[lang]/[country]/(website)/(home)/section-5.tsx rename to website/src/app/[lang]/[region]/(website)/(home)/section-5.tsx index c6ea5197b..1e503bc9e 100644 --- a/website/src/app/[lang]/[country]/(website)/(home)/section-5.tsx +++ b/website/src/app/[lang]/[region]/(website)/(home)/section-5.tsx @@ -1,5 +1,5 @@ -import { DefaultPageProps } from '@/app/[lang]/[country]'; -import { SectionCard } from '@/app/[lang]/[country]/(website)/(home)/section-5-card'; +import { DefaultPageProps } from '@/app/[lang]/[region]'; +import { SectionCard } from '@/app/[lang]/[region]/(website)/(home)/section-5-card'; import { Translator } from '@socialincome/shared/src/utils/i18n'; import { BaseContainer, Typography } from '@socialincome/ui'; diff --git a/website/src/app/[lang]/[country]/(website)/(home)/section-6-card.tsx b/website/src/app/[lang]/[region]/(website)/(home)/section-6-card.tsx similarity index 100% rename from website/src/app/[lang]/[country]/(website)/(home)/section-6-card.tsx rename to website/src/app/[lang]/[region]/(website)/(home)/section-6-card.tsx diff --git a/website/src/app/[lang]/[country]/(website)/(home)/section-6.tsx b/website/src/app/[lang]/[region]/(website)/(home)/section-6.tsx similarity index 95% rename from website/src/app/[lang]/[country]/(website)/(home)/section-6.tsx rename to website/src/app/[lang]/[region]/(website)/(home)/section-6.tsx index db17ba575..38410d9de 100644 --- a/website/src/app/[lang]/[country]/(website)/(home)/section-6.tsx +++ b/website/src/app/[lang]/[region]/(website)/(home)/section-6.tsx @@ -1,4 +1,4 @@ -import { DefaultPageProps } from '@/app/[lang]/[country]'; +import { DefaultPageProps } from '@/app/[lang]/[region]'; import { Translator } from '@socialincome/shared/src/utils/i18n'; import { BaseContainer, Typography } from '@socialincome/ui'; import { SectionCard } from './section-6-card'; diff --git a/website/src/app/[lang]/[country]/(website)/(home)/section-7-carousel.tsx b/website/src/app/[lang]/[region]/(website)/(home)/section-7-carousel.tsx similarity index 100% rename from website/src/app/[lang]/[country]/(website)/(home)/section-7-carousel.tsx rename to website/src/app/[lang]/[region]/(website)/(home)/section-7-carousel.tsx diff --git a/website/src/app/[lang]/[country]/(website)/(home)/section-7.tsx b/website/src/app/[lang]/[region]/(website)/(home)/section-7.tsx similarity index 85% rename from website/src/app/[lang]/[country]/(website)/(home)/section-7.tsx rename to website/src/app/[lang]/[region]/(website)/(home)/section-7.tsx index 77ea20e05..71f40a016 100644 --- a/website/src/app/[lang]/[country]/(website)/(home)/section-7.tsx +++ b/website/src/app/[lang]/[region]/(website)/(home)/section-7.tsx @@ -1,5 +1,5 @@ -import { DefaultPageProps } from '@/app/[lang]/[country]'; -import { Section7Carousel } from '@/app/[lang]/[country]/(website)/(home)/section-7-carousel'; +import { DefaultPageProps } from '@/app/[lang]/[region]'; +import { Section7Carousel } from '@/app/[lang]/[region]/(website)/(home)/section-7-carousel'; import { Translator } from '@socialincome/shared/src/utils/i18n'; import { BaseContainer, Typography } from '@socialincome/ui'; diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/ajla.jpg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/ajla.jpg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/ajla.jpg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/ajla.jpg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/alexandre.jpeg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/alexandre.jpeg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/alexandre.jpeg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/alexandre.jpeg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/anders.jpeg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/anders.jpeg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/anders.jpeg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/anders.jpeg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/andras.jpeg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/andras.jpeg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/andras.jpeg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/andras.jpeg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/anna-lina.jpeg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/anna-lina.jpeg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/anna-lina.jpeg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/anna-lina.jpeg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/anvita.jpeg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/anvita.jpeg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/anvita.jpeg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/anvita.jpeg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/aurelie.jpeg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/aurelie.jpeg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/aurelie.jpeg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/aurelie.jpeg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/carlos.jpeg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/carlos.jpeg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/carlos.jpeg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/carlos.jpeg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/change.gif b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/change.gif similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/change.gif rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/change.gif diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/flavien.jpeg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/flavien.jpeg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/flavien.jpeg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/flavien.jpeg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/juan.jpeg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/juan.jpeg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/juan.jpeg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/juan.jpeg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/julia.jpeg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/julia.jpeg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/julia.jpeg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/julia.jpeg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/kabelo.jpeg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/kabelo.jpeg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/kabelo.jpeg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/kabelo.jpeg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/kerrin.jpeg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/kerrin.jpeg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/kerrin.jpeg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/kerrin.jpeg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/larissa.jpeg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/larissa.jpeg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/larissa.jpeg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/larissa.jpeg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/lorenzo.jpg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/lorenzo.jpg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/lorenzo.jpg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/lorenzo.jpg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/mabel.jpeg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/mabel.jpeg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/mabel.jpeg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/mabel.jpeg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/mariatu.jpg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/mariatu.jpg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/mariatu.jpg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/mariatu.jpg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/marion.jpeg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/marion.jpeg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/marion.jpeg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/marion.jpeg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/michael.jpeg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/michael.jpeg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/michael.jpeg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/michael.jpeg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/mikolaj.jpeg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/mikolaj.jpeg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/mikolaj.jpeg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/mikolaj.jpeg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/patrik.jpeg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/patrik.jpeg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/patrik.jpeg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/patrik.jpeg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/rene.jpeg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/rene.jpeg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/rene.jpeg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/rene.jpeg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/riccardo.jpg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/riccardo.jpg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/riccardo.jpg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/riccardo.jpg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/sandino.jpg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/sandino.jpg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/sandino.jpg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/sandino.jpg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/sarah.jpeg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/sarah.jpeg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/sarah.jpeg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/sarah.jpeg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/sarvesh.jpeg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/sarvesh.jpeg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/sarvesh.jpeg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/sarvesh.jpeg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/simon.jpeg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/simon.jpeg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/simon.jpeg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/simon.jpeg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/simone.jpeg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/simone.jpeg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/simone.jpeg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/simone.jpeg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/thomas.jpeg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/thomas.jpeg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/thomas.jpeg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/thomas.jpeg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/verena.jpeg b/website/src/app/[lang]/[region]/(website)/about-us/(assets)/verena.jpeg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/about-us/team/(assets)/verena.jpeg rename to website/src/app/[lang]/[region]/(website)/about-us/(assets)/verena.jpeg diff --git a/website/src/app/[lang]/[country]/(website)/about-us/section-1.tsx b/website/src/app/[lang]/[region]/(website)/about-us/(sections)/section-1.tsx similarity index 80% rename from website/src/app/[lang]/[country]/(website)/about-us/section-1.tsx rename to website/src/app/[lang]/[region]/(website)/about-us/(sections)/section-1.tsx index a9c44fef4..a3b913e99 100644 --- a/website/src/app/[lang]/[country]/(website)/about-us/section-1.tsx +++ b/website/src/app/[lang]/[region]/(website)/about-us/(sections)/section-1.tsx @@ -1,8 +1,8 @@ -import { DefaultPageProps } from '@/app/[lang]/[country]'; +import { DefaultPageProps } from '@/app/[lang]/[region]'; import { Translator } from '@socialincome/shared/src/utils/i18n'; import { BaseContainer, Typography } from '@socialincome/ui'; import Image from 'next/image'; -import changeGif from './change.gif'; +import changeGif from '../(assets)/change.gif'; export default async function Section1({ params }: DefaultPageProps) { const translator = await Translator.getInstance({ @@ -11,7 +11,7 @@ export default async function Section1({ params }: DefaultPageProps) { }); return ( - + Change animation
diff --git a/website/src/app/[lang]/[country]/(website)/about-us/team/page.tsx b/website/src/app/[lang]/[region]/(website)/about-us/(sections)/team.tsx similarity index 64% rename from website/src/app/[lang]/[country]/(website)/about-us/team/page.tsx rename to website/src/app/[lang]/[region]/(website)/about-us/(sections)/team.tsx index 4df6b92fb..8e6309079 100644 --- a/website/src/app/[lang]/[country]/(website)/about-us/team/page.tsx +++ b/website/src/app/[lang]/[region]/(website)/about-us/(sections)/team.tsx @@ -1,39 +1,39 @@ -import { DefaultPageProps } from '@/app/[lang]/[country]'; +import { DefaultPageProps } from '@/app/[lang]/[region]'; +import ajlaImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/ajla.jpg'; +import alexandreImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/alexandre.jpeg'; +import andersImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/anders.jpeg'; +import andrasImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/andras.jpeg'; +import annalinaImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/anna-lina.jpeg'; +import anvitaImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/anvita.jpeg'; +import aurelieImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/aurelie.jpeg'; +import carlosImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/carlos.jpeg'; +import flavienImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/flavien.jpeg'; +import juanImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/juan.jpeg'; +import juliaImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/julia.jpeg'; +import kabeloImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/kabelo.jpeg'; +import kerrinImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/kerrin.jpeg'; +import larissaImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/larissa.jpeg'; +import lorenzoImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/lorenzo.jpg'; +import mabelImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/mabel.jpeg'; +import mariatuImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/mariatu.jpg'; +import marionImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/marion.jpeg'; +import michaelImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/michael.jpeg'; +import mikolajImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/mikolaj.jpeg'; +import patrikImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/patrik.jpeg'; +import reneImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/rene.jpeg'; +import riccardoImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/riccardo.jpg'; +import sandinoImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/sandino.jpg'; +import sarahImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/sarah.jpeg'; +import sarveshImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/sarvesh.jpeg'; +import simonImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/simon.jpeg'; +import simoneImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/simone.jpeg'; +import thomasImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/thomas.jpeg'; +import verenaImage from '@/app/[lang]/[region]/(website)/about-us/(assets)/verena.jpeg'; import { Translator } from '@socialincome/shared/src/utils/i18n'; import { BaseContainer, FontSize, Typography } from '@socialincome/ui'; import classNames from 'classnames'; import { StaticImport } from 'next/dist/shared/lib/get-img-props'; import Image from 'next/image'; -import ajlaImage from './(assets)/ajla.jpg'; -import alexandreImage from './(assets)/alexandre.jpeg'; -import andersImage from './(assets)/anders.jpeg'; -import andrasImage from './(assets)/andras.jpeg'; -import annalinaImage from './(assets)/anna-lina.jpeg'; -import anvitaImage from './(assets)/anvita.jpeg'; -import aurelieImage from './(assets)/aurelie.jpeg'; -import carlosImage from './(assets)/carlos.jpeg'; -import flavienImage from './(assets)/flavien.jpeg'; -import juanImage from './(assets)/juan.jpeg'; -import juliaImage from './(assets)/julia.jpeg'; -import kabeloImage from './(assets)/kabelo.jpeg'; -import kerrinImage from './(assets)/kerrin.jpeg'; -import larissaImage from './(assets)/larissa.jpeg'; -import lorenzoImage from './(assets)/lorenzo.jpg'; -import mabelImage from './(assets)/mabel.jpeg'; -import mariatuImage from './(assets)/mariatu.jpg'; -import marionImage from './(assets)/marion.jpeg'; -import michaelImage from './(assets)/michael.jpeg'; -import mikolajImage from './(assets)/mikolaj.jpeg'; -import patrikImage from './(assets)/patrik.jpeg'; -import reneImage from './(assets)/rene.jpeg'; -import riccardoImage from './(assets)/riccardo.jpg'; -import sandinoImage from './(assets)/sandino.jpg'; -import sarahImage from './(assets)/sarah.jpeg'; -import sarveshImage from './(assets)/sarvesh.jpeg'; -import simonImage from './(assets)/simon.jpeg'; -import simoneImage from './(assets)/simone.jpeg'; -import thomasImage from './(assets)/thomas.jpeg'; -import verenaImage from './(assets)/verena.jpeg'; type Group = { name: string; @@ -104,7 +104,7 @@ const groups: Group[] = [ ], }, ]; -export default async function Page({ params }: DefaultPageProps) { +export default async function Team({ params }: DefaultPageProps) { const translator = await Translator.getInstance({ language: params.lang, namespaces: ['countries', 'website-team'] }); return ( diff --git a/website/src/app/[lang]/[region]/(website)/about-us/page.tsx b/website/src/app/[lang]/[region]/(website)/about-us/page.tsx new file mode 100644 index 000000000..2d9c41f6a --- /dev/null +++ b/website/src/app/[lang]/[region]/(website)/about-us/page.tsx @@ -0,0 +1,12 @@ +import { DefaultPageProps } from '@/app/[lang]/[region]'; +import Team from '@/app/[lang]/[region]/(website)/about-us/(sections)/team'; +import Section1 from './(sections)/section-1'; + +export default async function Page(props: DefaultPageProps) { + return ( + <> + + + + ); +} diff --git a/website/src/app/[lang]/[country]/(website)/books/(assets)/better.jpg b/website/src/app/[lang]/[region]/(website)/books/(assets)/better.jpg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/books/(assets)/better.jpg rename to website/src/app/[lang]/[region]/(website)/books/(assets)/better.jpg diff --git a/website/src/app/[lang]/[country]/(website)/books/(assets)/coming.jpg b/website/src/app/[lang]/[region]/(website)/books/(assets)/coming.jpg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/books/(assets)/coming.jpg rename to website/src/app/[lang]/[region]/(website)/books/(assets)/coming.jpg diff --git a/website/src/app/[lang]/[country]/(website)/books/(assets)/divide.jpg b/website/src/app/[lang]/[region]/(website)/books/(assets)/divide.jpg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/books/(assets)/divide.jpg rename to website/src/app/[lang]/[region]/(website)/books/(assets)/divide.jpg diff --git a/website/src/app/[lang]/[country]/(website)/books/(assets)/freedom.jpg b/website/src/app/[lang]/[region]/(website)/books/(assets)/freedom.jpg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/books/(assets)/freedom.jpg rename to website/src/app/[lang]/[region]/(website)/books/(assets)/freedom.jpg diff --git a/website/src/app/[lang]/[country]/(website)/books/(assets)/hard.jpg b/website/src/app/[lang]/[region]/(website)/books/(assets)/hard.jpg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/books/(assets)/hard.jpg rename to website/src/app/[lang]/[region]/(website)/books/(assets)/hard.jpg diff --git a/website/src/app/[lang]/[country]/(website)/books/(assets)/life.jpg b/website/src/app/[lang]/[region]/(website)/books/(assets)/life.jpg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/books/(assets)/life.jpg rename to website/src/app/[lang]/[region]/(website)/books/(assets)/life.jpg diff --git a/website/src/app/[lang]/[country]/(website)/books/(assets)/lowrey.jpg b/website/src/app/[lang]/[region]/(website)/books/(assets)/lowrey.jpg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/books/(assets)/lowrey.jpg rename to website/src/app/[lang]/[region]/(website)/books/(assets)/lowrey.jpg diff --git a/website/src/app/[lang]/[country]/(website)/books/(assets)/poor-economics.jpg b/website/src/app/[lang]/[region]/(website)/books/(assets)/poor-economics.jpg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/books/(assets)/poor-economics.jpg rename to website/src/app/[lang]/[region]/(website)/books/(assets)/poor-economics.jpg diff --git a/website/src/app/[lang]/[country]/(website)/books/(assets)/utopia.jpg b/website/src/app/[lang]/[region]/(website)/books/(assets)/utopia.jpg similarity index 100% rename from website/src/app/[lang]/[country]/(website)/books/(assets)/utopia.jpg rename to website/src/app/[lang]/[region]/(website)/books/(assets)/utopia.jpg diff --git a/website/src/app/[lang]/[country]/(website)/books/page.tsx b/website/src/app/[lang]/[region]/(website)/books/page.tsx similarity index 78% rename from website/src/app/[lang]/[country]/(website)/books/page.tsx rename to website/src/app/[lang]/[region]/(website)/books/page.tsx index af3f20a78..7b3c69c28 100644 --- a/website/src/app/[lang]/[country]/(website)/books/page.tsx +++ b/website/src/app/[lang]/[region]/(website)/books/page.tsx @@ -1,4 +1,4 @@ -import { DefaultPageProps } from '@/app/[lang]/[country]'; +import { DefaultPageProps } from '@/app/[lang]/[region]'; import Section1 from './section-1'; import Section2 from './section-2'; diff --git a/website/src/app/[lang]/[country]/(website)/books/section-1.tsx b/website/src/app/[lang]/[region]/(website)/books/section-1.tsx similarity index 95% rename from website/src/app/[lang]/[country]/(website)/books/section-1.tsx rename to website/src/app/[lang]/[region]/(website)/books/section-1.tsx index c557862d3..183b4d273 100644 --- a/website/src/app/[lang]/[country]/(website)/books/section-1.tsx +++ b/website/src/app/[lang]/[region]/(website)/books/section-1.tsx @@ -1,4 +1,4 @@ -import { DefaultPageProps } from '@/app/[lang]/[country]'; +import { DefaultPageProps } from '@/app/[lang]/[region]'; import { ClockIcon } from '@heroicons/react/24/solid'; import { Translator } from '@socialincome/shared/src/utils/i18n'; import { BaseContainer, Typography } from '@socialincome/ui'; diff --git a/website/src/app/[lang]/[country]/(website)/books/section-2.tsx b/website/src/app/[lang]/[region]/(website)/books/section-2.tsx similarity index 98% rename from website/src/app/[lang]/[country]/(website)/books/section-2.tsx rename to website/src/app/[lang]/[region]/(website)/books/section-2.tsx index 868b3cda8..9e0240f9e 100644 --- a/website/src/app/[lang]/[country]/(website)/books/section-2.tsx +++ b/website/src/app/[lang]/[region]/(website)/books/section-2.tsx @@ -1,5 +1,5 @@ -import { DefaultPageProps } from '@/app/[lang]/[country]'; -import Book from '@/components/book/book'; +import { DefaultPageProps } from '@/app/[lang]/[region]'; +import Book from '@/components/ui/book'; import { Translator } from '@socialincome/shared/src/utils/i18n'; import { BaseContainer } from '@socialincome/ui'; import book6cover from './(assets)/better.jpg'; diff --git a/website/src/app/[lang]/[country]/(website)/donate/individual/page.tsx b/website/src/app/[lang]/[region]/(website)/donate/individual/page.tsx similarity index 97% rename from website/src/app/[lang]/[country]/(website)/donate/individual/page.tsx rename to website/src/app/[lang]/[region]/(website)/donate/individual/page.tsx index feff77d89..b0ace14b1 100644 --- a/website/src/app/[lang]/[country]/(website)/donate/individual/page.tsx +++ b/website/src/app/[lang]/[region]/(website)/donate/individual/page.tsx @@ -1,6 +1,6 @@ 'use client'; -import { DefaultPageProps } from '@/app/[lang]/[country]'; +import { DefaultPageProps } from '@/app/[lang]/[region]'; import { CreateSubscriptionData } from '@/app/api/stripe/checkout/new/route'; import { CheckCircleIcon } from '@heroicons/react/24/outline'; import { zodResolver } from '@hookform/resolvers/zod'; @@ -83,7 +83,7 @@ export default function Page({ params, searchParams }: DefaultPageProps) { const data: CreateSubscriptionData = { amount: values.amount * 100, // The amount is in cents, so we need to multiply by 100 to get the correct amount. intervalCount: Number(values.intervalCount), - successUrl: `${window.location.origin}/${params.lang}/${params.country}/donate/success?stripeCheckoutSessionId={CHECKOUT_SESSION_ID}`, + successUrl: `${window.location.origin}/${params.lang}/${params.region}/donate/success?stripeCheckoutSessionId={CHECKOUT_SESSION_ID}`, }; const response = await fetch('/api/stripe/checkout/new', { method: 'POST', diff --git a/website/src/app/[lang]/[country]/(website)/donate/success/create-user-form.tsx b/website/src/app/[lang]/[region]/(website)/donate/success/create-user-form.tsx similarity index 100% rename from website/src/app/[lang]/[country]/(website)/donate/success/create-user-form.tsx rename to website/src/app/[lang]/[region]/(website)/donate/success/create-user-form.tsx diff --git a/website/src/app/[lang]/[country]/(website)/donate/success/link-google-form.tsx b/website/src/app/[lang]/[region]/(website)/donate/success/link-google-form.tsx similarity index 100% rename from website/src/app/[lang]/[country]/(website)/donate/success/link-google-form.tsx rename to website/src/app/[lang]/[region]/(website)/donate/success/link-google-form.tsx diff --git a/website/src/app/[lang]/[country]/(website)/donate/success/page.tsx b/website/src/app/[lang]/[region]/(website)/donate/success/page.tsx similarity index 94% rename from website/src/app/[lang]/[country]/(website)/donate/success/page.tsx rename to website/src/app/[lang]/[region]/(website)/donate/success/page.tsx index c272fc439..9a0013f9a 100644 --- a/website/src/app/[lang]/[country]/(website)/donate/success/page.tsx +++ b/website/src/app/[lang]/[region]/(website)/donate/success/page.tsx @@ -1,4 +1,4 @@ -import { DefaultPageProps } from '@/app/[lang]/[country]'; +import { DefaultPageProps } from '@/app/[lang]/[region]'; import { initializeStripe } from '@socialincome/shared/src/stripe'; import { BaseContainer, Typography } from '@socialincome/ui'; import { CreateUserForm } from './create-user-form'; diff --git a/website/src/app/[lang]/[country]/(website)/faq/faq-section.tsx b/website/src/app/[lang]/[region]/(website)/faq/faq-section.tsx similarity index 100% rename from website/src/app/[lang]/[country]/(website)/faq/faq-section.tsx rename to website/src/app/[lang]/[region]/(website)/faq/faq-section.tsx diff --git a/website/src/app/[lang]/[country]/(website)/faq/page.tsx b/website/src/app/[lang]/[region]/(website)/faq/page.tsx similarity index 98% rename from website/src/app/[lang]/[country]/(website)/faq/page.tsx rename to website/src/app/[lang]/[region]/(website)/faq/page.tsx index 613d244e8..9d1065935 100644 --- a/website/src/app/[lang]/[country]/(website)/faq/page.tsx +++ b/website/src/app/[lang]/[region]/(website)/faq/page.tsx @@ -1,4 +1,4 @@ -import { DefaultPageProps } from '@/app/[lang]/[country]'; +import { DefaultPageProps } from '@/app/[lang]/[region]'; import { Translator } from '@socialincome/shared/src/utils/i18n'; import { BaseContainer, Typography } from '@socialincome/ui'; import { FAQSection } from './faq-section'; diff --git a/website/src/app/[lang]/[region]/(website)/layout.tsx b/website/src/app/[lang]/[region]/(website)/layout.tsx new file mode 100644 index 000000000..a994e274b --- /dev/null +++ b/website/src/app/[lang]/[region]/(website)/layout.tsx @@ -0,0 +1,18 @@ +import { DefaultLayoutProps } from '@/app/[lang]/[region]'; +import Footer from '@/components/footer/footer'; +import Navbar from '@/components/navbar/navbar'; +import { websiteLanguages, websiteRegions } from '@/i18n'; +import { PropsWithChildren } from 'react'; + +export const generateStaticParams = () => + websiteRegions.flatMap((country) => websiteLanguages.map((lang) => ({ lang, country }))); + +export default function Layout({ children, params }: PropsWithChildren) { + return ( +
+ +
{children}
+
+
+ ); +} diff --git a/website/src/app/[lang]/[country]/(website)/login/layout.tsx b/website/src/app/[lang]/[region]/(website)/login/layout.tsx similarity index 51% rename from website/src/app/[lang]/[country]/(website)/login/layout.tsx rename to website/src/app/[lang]/[region]/(website)/login/layout.tsx index 37adb2672..44c1323a8 100644 --- a/website/src/app/[lang]/[country]/(website)/login/layout.tsx +++ b/website/src/app/[lang]/[region]/(website)/login/layout.tsx @@ -1,7 +1,7 @@ -import { DefaultLayoutProps } from '@/app/[lang]/[country]'; +import { DefaultLayoutProps } from '@/app/[lang]/[region]'; import { BaseContainer } from '@socialincome/ui'; import { PropsWithChildren } from 'react'; -export default async function Layout({ children, params }: PropsWithChildren) { +export default async function Layout({ children }: PropsWithChildren) { return {children}; } diff --git a/website/src/app/[lang]/[country]/(website)/login/login-form.tsx b/website/src/app/[lang]/[region]/(website)/login/login-form.tsx similarity index 96% rename from website/src/app/[lang]/[country]/(website)/login/login-form.tsx rename to website/src/app/[lang]/[region]/(website)/login/login-form.tsx index 39e42db57..cd151c22f 100644 --- a/website/src/app/[lang]/[country]/(website)/login/login-form.tsx +++ b/website/src/app/[lang]/[region]/(website)/login/login-form.tsx @@ -1,6 +1,6 @@ 'use client'; -import { DefaultPageProps } from '@/app/[lang]/[country]'; +import { DefaultPageProps } from '@/app/[lang]/[region]'; import { zodResolver } from '@hookform/resolvers/zod'; import { SiGoogle } from '@icons-pack/react-simple-icons'; import { Button, Form, FormControl, FormField, FormItem, FormMessage, Input, Typography } from '@socialincome/ui'; @@ -47,7 +47,7 @@ export default function LoginForm({ params, translations }: LoginFormProps) { await auth.setPersistence(browserSessionPersistence); await signInWithEmailAndPassword(auth, values.email, values.password) .then(() => { - router.push(`/${params.lang}/${params.country}/me`); + router.push(`/${params.lang}/${params.region}/me`); }) .catch((error: FirebaseError) => { error.code === 'auth/wrong-password' && toast.error(translations.wrongPassword); diff --git a/website/src/app/[lang]/[country]/(website)/login/page.tsx b/website/src/app/[lang]/[region]/(website)/login/page.tsx similarity index 85% rename from website/src/app/[lang]/[country]/(website)/login/page.tsx rename to website/src/app/[lang]/[region]/(website)/login/page.tsx index 3f023c1c9..e5c03f06c 100644 --- a/website/src/app/[lang]/[country]/(website)/login/page.tsx +++ b/website/src/app/[lang]/[region]/(website)/login/page.tsx @@ -1,5 +1,5 @@ -import { DefaultPageProps } from '@/app/[lang]/[country]'; -import LoginForm from '@/app/[lang]/[country]/(website)/login/login-form'; +import { DefaultPageProps } from '@/app/[lang]/[region]'; +import LoginForm from '@/app/[lang]/[region]/(website)/login/login-form'; import { Translator } from '@socialincome/shared/src/utils/i18n'; export default async function Page(props: DefaultPageProps) { diff --git a/website/src/app/[lang]/[country]/(website)/login/password-reset/page.tsx b/website/src/app/[lang]/[region]/(website)/login/password-reset/page.tsx similarity index 94% rename from website/src/app/[lang]/[country]/(website)/login/password-reset/page.tsx rename to website/src/app/[lang]/[region]/(website)/login/password-reset/page.tsx index cc18f8745..b02c33504 100644 --- a/website/src/app/[lang]/[country]/(website)/login/password-reset/page.tsx +++ b/website/src/app/[lang]/[region]/(website)/login/password-reset/page.tsx @@ -1,6 +1,5 @@ 'use client'; -import { DefaultPageProps } from '@/app/[lang]/[country]'; import { zodResolver } from '@hookform/resolvers/zod'; import { Button, Form, FormControl, FormField, FormItem, FormMessage, Input, Typography } from '@socialincome/ui'; import { FirebaseError } from 'firebase/app'; @@ -11,7 +10,7 @@ import { useAuth } from 'reactfire'; import * as z from 'zod'; // TODO: i18n -export default function Page({ params }: DefaultPageProps) { +export default function Page() { const auth = useAuth(); const formSchema = z.object({ diff --git a/website/src/app/[lang]/[country]/(website)/me/contact-details/page.tsx b/website/src/app/[lang]/[region]/(website)/me/contact-details/page.tsx similarity index 98% rename from website/src/app/[lang]/[country]/(website)/me/contact-details/page.tsx rename to website/src/app/[lang]/[region]/(website)/me/contact-details/page.tsx index 6df201cc5..5b497d9ee 100644 --- a/website/src/app/[lang]/[country]/(website)/me/contact-details/page.tsx +++ b/website/src/app/[lang]/[region]/(website)/me/contact-details/page.tsx @@ -1,8 +1,8 @@ 'use client'; -import { UserContext } from '@/app/[lang]/[country]/(website)/me/user-context-provider'; +import { UserContext } from '@/app/[lang]/[region]/(website)/me/user-context-provider'; import { zodResolver } from '@hookform/resolvers/zod'; -import { USER_FIRESTORE_PATH } from '@socialincome/shared/src/types/User'; +import { USER_FIRESTORE_PATH } from '@socialincome/shared/src/types/user'; import { Button, Form, diff --git a/website/src/app/[lang]/[country]/(website)/me/contributions/billing-portal-button.tsx b/website/src/app/[lang]/[region]/(website)/me/contributions/billing-portal-button.tsx similarity index 70% rename from website/src/app/[lang]/[country]/(website)/me/contributions/billing-portal-button.tsx rename to website/src/app/[lang]/[region]/(website)/me/contributions/billing-portal-button.tsx index d0e241f70..4c632d0e7 100644 --- a/website/src/app/[lang]/[country]/(website)/me/contributions/billing-portal-button.tsx +++ b/website/src/app/[lang]/[region]/(website)/me/contributions/billing-portal-button.tsx @@ -1,14 +1,16 @@ 'use client'; import { Button } from '@socialincome/ui'; +import { signOut } from 'firebase/auth'; import { useRouter } from 'next/navigation'; import { useEffect, useState } from 'react'; -import { useUser } from 'reactfire'; +import { useAuth, useUser } from 'reactfire'; import Stripe from 'stripe'; // TODO: i18n export function BillingPortalButton() { const router = useRouter(); + const auth = useAuth(); const { data: authUser } = useUser(); const [billingPortalUrl, setBillingPortalUrl] = useState(null); @@ -23,8 +25,11 @@ export function BillingPortalButton() { }, [authUser]); return ( - +
+ + +
); } diff --git a/website/src/app/[lang]/[country]/(website)/me/contributions/contributions-table.tsx b/website/src/app/[lang]/[region]/(website)/me/contributions/contributions-table.tsx similarity index 91% rename from website/src/app/[lang]/[country]/(website)/me/contributions/contributions-table.tsx rename to website/src/app/[lang]/[region]/(website)/me/contributions/contributions-table.tsx index f6b8bbde9..453b9b3c1 100644 --- a/website/src/app/[lang]/[country]/(website)/me/contributions/contributions-table.tsx +++ b/website/src/app/[lang]/[region]/(website)/me/contributions/contributions-table.tsx @@ -1,10 +1,10 @@ 'use client'; -import { DefaultParams } from '@/app/[lang]/[country]'; -import { UserContext } from '@/app/[lang]/[country]/(website)/me/user-context-provider'; +import { DefaultParams } from '@/app/[lang]/[region]'; +import { UserContext } from '@/app/[lang]/[region]/(website)/me/user-context-provider'; import { useTranslator } from '@/hooks/useTranslator'; -import { CONTRIBUTION_FIRESTORE_PATH, StatusKey } from '@socialincome/shared/src/types/Contribution'; -import { USER_FIRESTORE_PATH } from '@socialincome/shared/src/types/User'; +import { CONTRIBUTION_FIRESTORE_PATH, StatusKey } from '@socialincome/shared/src/types/contribution'; +import { USER_FIRESTORE_PATH } from '@socialincome/shared/src/types/user'; import { toDateTime } from '@socialincome/shared/src/utils/date'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Typography } from '@socialincome/ui'; import { useQuery } from '@tanstack/react-query'; diff --git a/website/src/app/[lang]/[country]/(website)/me/contributions/page.tsx b/website/src/app/[lang]/[region]/(website)/me/contributions/page.tsx similarity index 79% rename from website/src/app/[lang]/[country]/(website)/me/contributions/page.tsx rename to website/src/app/[lang]/[region]/(website)/me/contributions/page.tsx index 77ad35c88..3c4c0d506 100644 --- a/website/src/app/[lang]/[country]/(website)/me/contributions/page.tsx +++ b/website/src/app/[lang]/[region]/(website)/me/contributions/page.tsx @@ -1,5 +1,5 @@ -import { DefaultPageProps } from '@/app/[lang]/[country]'; -import { ContributionsTable } from '@/app/[lang]/[country]/(website)/me/contributions/contributions-table'; +import { DefaultPageProps } from '@/app/[lang]/[region]'; +import { ContributionsTable } from '@/app/[lang]/[region]/(website)/me/contributions/contributions-table'; import { Translator } from '@socialincome/shared/src/utils/i18n'; import { BillingPortalButton } from './billing-portal-button'; diff --git a/website/src/app/[lang]/[country]/(website)/me/layout-tab.tsx b/website/src/app/[lang]/[region]/(website)/me/layout-tab.tsx similarity index 100% rename from website/src/app/[lang]/[country]/(website)/me/layout-tab.tsx rename to website/src/app/[lang]/[region]/(website)/me/layout-tab.tsx diff --git a/website/src/app/[lang]/[country]/(website)/me/layout.tsx b/website/src/app/[lang]/[region]/(website)/me/layout.tsx similarity index 74% rename from website/src/app/[lang]/[country]/(website)/me/layout.tsx rename to website/src/app/[lang]/[region]/(website)/me/layout.tsx index 7fd1f08bd..5746fd81b 100644 --- a/website/src/app/[lang]/[country]/(website)/me/layout.tsx +++ b/website/src/app/[lang]/[region]/(website)/me/layout.tsx @@ -1,5 +1,5 @@ -import { DefaultLayoutProps } from '@/app/[lang]/[country]'; -import { UserContextProvider } from '@/app/[lang]/[country]/(website)/me/user-context-provider'; +import { DefaultLayoutProps } from '@/app/[lang]/[region]'; +import { UserContextProvider } from '@/app/[lang]/[region]/(website)/me/user-context-provider'; import { Translator } from '@socialincome/shared/src/utils/i18n'; import { BaseContainer } from '@socialincome/ui'; import { PropsWithChildren } from 'react'; @@ -10,11 +10,11 @@ export default async function Layout({ children, params }: PropsWithChildren { + if (currency && props.currency !== currency) { + redirect('./' + currency.toLowerCase()); + } + }, [currency, props.currency]); + + return null; +} diff --git a/website/src/app/[lang]/[country]/(website)/transparency/finances/[currency]/info-card.tsx b/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/info-card.tsx similarity index 100% rename from website/src/app/[lang]/[country]/(website)/transparency/finances/[currency]/info-card.tsx rename to website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/info-card.tsx diff --git a/website/src/app/[lang]/[country]/(website)/transparency/finances/[currency]/page.tsx b/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/page.tsx similarity index 80% rename from website/src/app/[lang]/[country]/(website)/transparency/finances/[currency]/page.tsx rename to website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/page.tsx index bdcc62c8c..0c8c02df9 100644 --- a/website/src/app/[lang]/[country]/(website)/transparency/finances/[currency]/page.tsx +++ b/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/page.tsx @@ -1,6 +1,7 @@ -import { DefaultPageProps, DefaultParams } from '@/app/[lang]/[country]'; +import { DefaultPageProps, DefaultParams } from '@/app/[lang]/[region]'; +import { CurrencyRedirect } from '@/app/[lang]/[region]/(website)/transparency/finances/[currency]/currency-redirect'; import { firestoreAdmin } from '@/firebase-admin'; -import { ValidCountry, WebsiteLanguage } from '@/i18n'; +import { WebsiteCurrency, WebsiteLanguage, WebsiteRegion } from '@/i18n'; import { ContributionStats, ContributionStatsCalculator, @@ -17,7 +18,7 @@ export const generateStaticParams = () => ['USD', 'CHF'].map((currency) => ({ cu export type TransparencyPageProps = { params: { - country: ValidCountry; + country: WebsiteRegion; lang: WebsiteLanguage; currency: string; }; @@ -37,10 +38,13 @@ export default async function Page({ params }: TransparencyPageProps) { const paymentStats = paymentCalculator.allStats(); return { contributionStats, paymentStats }; }; - const { contributionStats, paymentStats } = await getStats(params.currency.toUpperCase()); + + const currency = params.currency.toUpperCase() as WebsiteCurrency; + const { contributionStats, paymentStats } = await getStats(currency); return ( +
diff --git a/website/src/app/[lang]/[country]/(website)/transparency/finances/[currency]/section-1.tsx b/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-1.tsx similarity index 100% rename from website/src/app/[lang]/[country]/(website)/transparency/finances/[currency]/section-1.tsx rename to website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-1.tsx diff --git a/website/src/app/[lang]/[country]/(website)/transparency/finances/[currency]/section-2.tsx b/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-2.tsx similarity index 100% rename from website/src/app/[lang]/[country]/(website)/transparency/finances/[currency]/section-2.tsx rename to website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-2.tsx diff --git a/website/src/app/[lang]/[country]/(website)/transparency/finances/[currency]/section-3-cards.tsx b/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-3-cards.tsx similarity index 100% rename from website/src/app/[lang]/[country]/(website)/transparency/finances/[currency]/section-3-cards.tsx rename to website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-3-cards.tsx diff --git a/website/src/app/[lang]/[country]/(website)/transparency/finances/[currency]/section-3.tsx b/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-3.tsx similarity index 100% rename from website/src/app/[lang]/[country]/(website)/transparency/finances/[currency]/section-3.tsx rename to website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-3.tsx diff --git a/website/src/app/[lang]/[country]/(website)/transparency/finances/[currency]/section-4.tsx b/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-4.tsx similarity index 99% rename from website/src/app/[lang]/[country]/(website)/transparency/finances/[currency]/section-4.tsx rename to website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-4.tsx index dfcfdce2f..1b99f0831 100644 --- a/website/src/app/[lang]/[country]/(website)/transparency/finances/[currency]/section-4.tsx +++ b/website/src/app/[lang]/[region]/(website)/transparency/finances/[currency]/section-4.tsx @@ -1,7 +1,7 @@ import { firestoreAdmin } from '@/firebase-admin'; import { InformationCircleIcon } from '@heroicons/react/24/outline'; import { BanknotesIcon, BuildingLibraryIcon, DevicePhoneMobileIcon } from '@heroicons/react/24/solid'; -import { PAYMENT_AMOUNT } from '@socialincome/shared/src/types/Payment'; +import { PAYMENT_AMOUNT } from '@socialincome/shared/src/types/payment'; import { getLatestExchangeRate } from '@socialincome/shared/src/utils/exchangeRates'; import { Translator } from '@socialincome/shared/src/utils/i18n'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, Typography } from '@socialincome/ui'; diff --git a/website/src/app/[lang]/[region]/(website)/transparency/finances/page.tsx b/website/src/app/[lang]/[region]/(website)/transparency/finances/page.tsx new file mode 100644 index 000000000..a8a9540ea --- /dev/null +++ b/website/src/app/[lang]/[region]/(website)/transparency/finances/page.tsx @@ -0,0 +1,13 @@ +'use client'; + +import { useI18n } from '@/app/providers/i18n-provider-client'; +import { redirect } from 'next/navigation'; +import { useEffect } from 'react'; + +export default function Page() { + const { currency } = useI18n(); + + useEffect(() => { + redirect('./finances/' + currency?.toLowerCase()); + }, [currency]); +} diff --git a/website/src/app/[lang]/[country]/(website)/transparency/recipient-selection/page.tsx b/website/src/app/[lang]/[region]/(website)/transparency/recipient-selection/page.tsx similarity index 100% rename from website/src/app/[lang]/[country]/(website)/transparency/recipient-selection/page.tsx rename to website/src/app/[lang]/[region]/(website)/transparency/recipient-selection/page.tsx diff --git a/website/src/app/[lang]/[country]/index.ts b/website/src/app/[lang]/[region]/index.ts similarity index 52% rename from website/src/app/[lang]/[country]/index.ts rename to website/src/app/[lang]/[region]/index.ts index 23322fd31..c71484942 100644 --- a/website/src/app/[lang]/[country]/index.ts +++ b/website/src/app/[lang]/[region]/index.ts @@ -1,8 +1,12 @@ -import { ValidCountry, WebsiteLanguage } from '@/i18n'; +import { WebsiteLanguage, WebsiteRegion } from '@/i18n'; + +export const LANGUAGE_COOKIE = 'si_lang'; +export const REGION_COOKIE = 'si_region'; +export const CURRENCY_COOKIE = 'si_currency'; export interface DefaultParams { lang: WebsiteLanguage; - country: ValidCountry; + region: WebsiteRegion; } export interface DefaultLayoutProps { diff --git a/website/src/app/api/stripe/billing-portal/route.ts b/website/src/app/api/stripe/billing-portal/route.ts index b2a6a35c6..169d4e2f6 100644 --- a/website/src/app/api/stripe/billing-portal/route.ts +++ b/website/src/app/api/stripe/billing-portal/route.ts @@ -1,6 +1,6 @@ import { authAdmin, firestoreAdmin } from '@/firebase-admin'; import { initializeStripe } from '@socialincome/shared/src/stripe'; -import { USER_FIRESTORE_PATH, User } from '@socialincome/shared/src/types/User'; +import { USER_FIRESTORE_PATH, User } from '@socialincome/shared/src/types/user'; import { NextResponse } from 'next/server'; export async function GET(request: Request) { diff --git a/website/src/app/api/user/create-auth-user/route.ts b/website/src/app/api/user/create-auth-user/route.ts index 407224f28..27045abdd 100644 --- a/website/src/app/api/user/create-auth-user/route.ts +++ b/website/src/app/api/user/create-auth-user/route.ts @@ -1,5 +1,5 @@ import { authAdmin, firestoreAdmin } from '@/firebase-admin'; -import { USER_FIRESTORE_PATH, User } from '@socialincome/shared/src/types/User'; +import { USER_FIRESTORE_PATH, User } from '@socialincome/shared/src/types/user'; import { NextResponse } from 'next/server'; export type ResetPasswordData = { diff --git a/website/src/app/layout.tsx b/website/src/app/layout.tsx index 9c1039a60..62349b05f 100644 --- a/website/src/app/layout.tsx +++ b/website/src/app/layout.tsx @@ -1,4 +1,5 @@ -import { Providers } from '@/app/providers'; +import { I18nProvider } from '@/app/providers/i18n-provider'; +import { ProvidersClient } from '@/app/providers/providers-client'; import { PropsWithChildren } from 'react'; import './globals.css'; @@ -10,7 +11,9 @@ export const metadata = { export default function RootLayout({ children }: PropsWithChildren) { return ( - {children} + + {children} + ); } diff --git a/website/src/app/providers/i18n-provider-client.tsx b/website/src/app/providers/i18n-provider-client.tsx new file mode 100644 index 000000000..e49fc575d --- /dev/null +++ b/website/src/app/providers/i18n-provider-client.tsx @@ -0,0 +1,68 @@ +'use client'; + +import { CURRENCY_COOKIE, LANGUAGE_COOKIE, REGION_COOKIE } from '@/app/[lang]/[region]'; +import { useCookieState } from '@/hooks/useCookieState'; +import { WebsiteCurrency, WebsiteLanguage, WebsiteRegion } from '@/i18n'; +import { useRouter, useSearchParams } from 'next/navigation'; +import { PropsWithChildren, createContext, useContext, useEffect } from 'react'; + +type I18nContextType = { + language: WebsiteLanguage | undefined; + setLanguage: (language: WebsiteLanguage) => void; + region: WebsiteRegion | undefined; + setRegion: (country: WebsiteRegion) => void; + currency: WebsiteCurrency | undefined; + setCurrency: (currency: WebsiteCurrency) => void; +}; + +const I18nContext = createContext(undefined!); +export const useI18n = () => useContext(I18nContext); + +type I18nProviderClientProps = { + cookies: Map; +}; + +export function I18nProviderClient({ cookies, children }: PropsWithChildren) { + const { value: language, setCookie: setLanguage } = useCookieState(LANGUAGE_COOKIE); + const { value: region, setCookie: setRegion } = useCookieState(REGION_COOKIE); + const { value: currency, setCookie: setCurrency } = useCookieState( + CURRENCY_COOKIE, + cookies.get(CURRENCY_COOKIE) as WebsiteCurrency, + ); + + const router = useRouter(); + const searchParams = useSearchParams(); + + useEffect(() => { + const pathSegments = window.location.pathname.split('/'); + if (language && pathSegments[1] !== language) { + pathSegments[1] = language; + const current = new URLSearchParams(Array.from(searchParams.entries())); + router.push(pathSegments.join('/') + '?' + current.toString()); + } + }, [language, router, searchParams]); + + useEffect(() => { + const pathSegments = window.location.pathname.split('/'); + if (region && pathSegments[2] !== region) { + pathSegments[2] = region; + const current = new URLSearchParams(Array.from(searchParams.entries())); + router.push(pathSegments.join('/') + '?' + current.toString()); + } + }, [region, router, searchParams]); + + return ( + setLanguage(language, { expires: 365 }), + region: region, + setRegion: (country) => setRegion(country, { expires: 365 }), + currency: currency, + setCurrency: (currency) => setCurrency(currency, { expires: 365 }), + }} + > + {children} + + ); +} diff --git a/website/src/app/providers/i18n-provider.tsx b/website/src/app/providers/i18n-provider.tsx new file mode 100644 index 000000000..7863bd45b --- /dev/null +++ b/website/src/app/providers/i18n-provider.tsx @@ -0,0 +1,17 @@ +import { I18nProviderClient } from '@/app/providers/i18n-provider-client'; +import { cookies } from 'next/headers'; +import { PropsWithChildren } from 'react'; + +export function I18nProvider({ children }: PropsWithChildren) { + /** + * The I18nProviderClient is wrapped into this component so that the values of the cookies are also available on SSR. + * Without this, the HTML rendered on the client can be different from the HTML rendered on the server, which causes + * hydration issues. + */ + const cookiesMap = new Map( + cookies() + .getAll() + .map((obj) => [obj.name, obj.value]), + ); + return {children}; +} diff --git a/website/src/app/providers.tsx b/website/src/app/providers/providers-client.tsx similarity index 98% rename from website/src/app/providers.tsx rename to website/src/app/providers/providers-client.tsx index 7aff92134..4f5295a66 100644 --- a/website/src/app/providers.tsx +++ b/website/src/app/providers/providers-client.tsx @@ -111,7 +111,7 @@ export function ThemeProvider({ children }: PropsWithChildren) { return {children}; } -export function Providers({ children }: PropsWithChildren) { +export function ProvidersClient({ children }: PropsWithChildren) { const firebaseConfig = { apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID, diff --git a/website/src/components/country-flags.tsx b/website/src/components/country-flags.tsx new file mode 100644 index 000000000..fcd81e91b --- /dev/null +++ b/website/src/components/country-flags.tsx @@ -0,0 +1,19 @@ +import { WebsiteCurrency } from '@/i18n'; +import { CH, EU } from 'country-flag-icons/react/1x1'; +import { SL, US } from 'country-flag-icons/react/3x2'; +import { Component } from 'react'; + +export function getFlagComponentByCurrency(currency: WebsiteCurrency | undefined) { + switch (currency) { + case 'USD': + return US; + case 'CHF': + return CH; + case 'EUR': + return EU; + case 'SLE': + return SL; + case undefined: + return Component; + } +} diff --git a/website/src/components/footer/footer-client.tsx b/website/src/components/footer/footer-client.tsx index a4fe5d13e..c619e5990 100644 --- a/website/src/components/footer/footer-client.tsx +++ b/website/src/components/footer/footer-client.tsx @@ -1,10 +1,9 @@ 'use client'; -import { DefaultLayoutProps } from '@/app/[lang]/[country]'; -import { onLanguageChange } from '@/components/navbar/language-switcher'; -import { ValidCountry } from '@/i18n'; +import { DefaultParams } from '@/app/[lang]/[region]'; +import { useI18n } from '@/app/providers/i18n-provider-client'; +import { WebsiteLanguage, WebsiteRegion } from '@/i18n'; import { GlobeAltIcon, LanguageIcon } from '@heroicons/react/24/solid'; -import { LanguageCode } from '@socialincome/shared/src/types/Language'; import { Select, SelectContent, @@ -14,37 +13,23 @@ import { SelectTrigger, SelectValue, } from '@socialincome/ui'; -import { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime'; -import { ReadonlyURLSearchParams, useRouter, useSearchParams } from 'next/navigation'; import { Suspense } from 'react'; type FooterClientProps = { - supportedTranslatedLanguages: { code: LanguageCode; translation: string }[]; - supportedTranslatedCountries: { code: ValidCountry; translation: string }[]; -} & DefaultLayoutProps; - -function onCountryChange(country: ValidCountry, router: AppRouterInstance, searchParams: ReadonlyURLSearchParams) { - const pathSegments = window.location.pathname.split('/'); - pathSegments[2] = country; - const current = new URLSearchParams(Array.from(searchParams.entries())); - router.push(pathSegments.join('/') + '?' + current.toString()); -} + languages: { code: WebsiteLanguage; translation: string }[]; + regions: { code: WebsiteRegion; translation: string }[]; +} & DefaultParams; // TODO: i18n -function FooterComponent({ params, supportedTranslatedLanguages, supportedTranslatedCountries }: FooterClientProps) { - const router = useRouter(); - const searchParams = useSearchParams(); +function FooterComponent({ lang, region, languages, regions }: FooterClientProps) { + const { setLanguage, setRegion } = useI18n(); - const initialCountry = supportedTranslatedCountries.find( - (candidateCountry) => candidateCountry.code === params.country, - ); - const initialLanguage = supportedTranslatedLanguages.find( - (candidateLanguage) => candidateLanguage.code === params.lang, - ); + const initialCountry = regions.find((candidateCountry) => candidateCountry.code === region); + const initialLanguage = languages.find((candidateLanguage) => candidateLanguage.code === lang); return (
- setLanguage(l)}> @@ -52,7 +37,7 @@ function FooterComponent({ params, supportedTranslatedLanguages, supportedTransl Language - {supportedTranslatedLanguages.map((supportedLanguage, index) => ( + {languages.map((supportedLanguage, index) => ( {supportedLanguage.translation} @@ -60,7 +45,7 @@ function FooterComponent({ params, supportedTranslatedLanguages, supportedTransl - setRegion(c)}> @@ -68,7 +53,7 @@ function FooterComponent({ params, supportedTranslatedLanguages, supportedTransl Country - {supportedTranslatedCountries.map((country, index) => ( + {regions.map((country, index) => ( {country.translation} diff --git a/website/src/components/footer/footer.tsx b/website/src/components/footer/footer.tsx index ab5ee7329..e5f213c75 100644 --- a/website/src/components/footer/footer.tsx +++ b/website/src/components/footer/footer.tsx @@ -1,10 +1,6 @@ -import { DefaultLayoutProps } from '@/app/[lang]/[country]'; -import { LanguageCode } from '@socialincome/shared/src/types/Language'; -import { Translator } from '@socialincome/shared/src/utils/i18n'; - import { FooterClient } from '@/components/footer/footer-client'; import { SILogo } from '@/components/logos/si-logo'; -import { ValidCountry } from '@/i18n'; +import { websiteLanguages, websiteRegions } from '@/i18n'; import { BriefcaseIcon, EnvelopeIcon, @@ -16,9 +12,11 @@ import { } from '@heroicons/react/24/solid'; import { SiFacebook, SiInstagram, SiLinkedin, SiTwitter } from '@icons-pack/react-simple-icons'; import { IconType } from '@icons-pack/react-simple-icons/types'; +import { Translator } from '@socialincome/shared/src/utils/i18n'; import { BaseContainer, Typography } from '@socialincome/ui'; import Link from 'next/link'; import { HTMLAttributeAnchorTarget } from 'react'; +import { DefaultParams } from '../../app/[lang]/[region]'; type FooterLinkProps = { label: string; @@ -38,35 +36,31 @@ function FooterLink({ label, url, Icon, target = '_self' }: FooterLinkProps) { ); } -type FooterProps = { - supportedLanguages: LanguageCode[]; - supportedCountries: ValidCountry[]; -} & DefaultLayoutProps; - // TODO: i18n -export default async function Footer({ params, supportedLanguages, supportedCountries }: FooterProps) { +export default async function Footer({ lang, region }: DefaultParams) { const translator = await Translator.getInstance({ - language: params.lang, - namespaces: ['common', 'website-common', 'website-me', 'countries'], + language: lang, + namespaces: ['common', 'website-common', 'website-me'], }); - const supportedTranslatedLanguages = supportedLanguages.map((lang) => { + const supportedTranslatedLanguages = websiteLanguages.map((lang) => { return { translation: translator.t(`languages.${lang}`), code: lang }; }); - const supportedTranslatedCountries = supportedCountries.map((country) => { - return { translation: translator.t(`${country.toUpperCase()}`), code: country }; + const supportedTranslatedCountries = websiteRegions.map((region) => { + return { translation: translator.t(`regions.${region}`), code: region }; }); return (
-
+
@@ -96,59 +90,47 @@ export default async function Footer({ params, supportedLanguages, supportedCoun url="https://www.linkedin.com/company/socialincome" target="_blank" /> - +
Resources - - - - + + + +
Our Work - - - - + + + +
About us - - - - + + + +
diff --git a/website/src/components/i18n-dialog.tsx b/website/src/components/i18n-dialog.tsx new file mode 100644 index 000000000..9ffaff46e --- /dev/null +++ b/website/src/components/i18n-dialog.tsx @@ -0,0 +1,122 @@ +'use client'; + +import { useI18n } from '@/app/providers/i18n-provider-client'; +import { WebsiteCurrency, WebsiteLanguage, WebsiteRegion } from '@/i18n'; +import { CurrencyDollarIcon } from '@heroicons/react/24/outline'; +import { GlobeEuropeAfricaIcon, LanguageIcon } from '@heroicons/react/24/solid'; +import { LanguageCode } from '@socialincome/shared/src/types/language'; +import { + Dialog, + DialogContent, + DialogTrigger, + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectTrigger, + SelectValue, +} from '@socialincome/ui'; +import _ from 'lodash'; +import { PropsWithChildren, useState } from 'react'; +import { Currency } from '../../../shared/src/types/currency'; + +type I18nDialogProps = { + languages: { + code: LanguageCode; + translation: string; + }[]; + regions: { + code: string; + translation: string; + }[]; + currencies: { + code: Currency; + translation: string; + }[]; + translations: { + language: string; + currentLanguage: string; + region: string; + currentRegion: string; + currency: string; + currentCurrency: string; + }; +}; + +export function I18nDialog( + { languages, regions, currencies, translations, children }: PropsWithChildren, +) { + const [open, setOpen] = useState(false); + const { language, setLanguage, region, setRegion, currency, setCurrency } = useI18n(); + + return ( + + {children} + + {!_.isEmpty(languages) && ( + + )} + + {!_.isEmpty(regions) && ( + + )} + + {!_.isEmpty(currencies) && ( + + )} + + + ); +} diff --git a/website/src/components/navbar/language-switcher.tsx b/website/src/components/navbar/language-switcher.tsx deleted file mode 100644 index a03681367..000000000 --- a/website/src/components/navbar/language-switcher.tsx +++ /dev/null @@ -1,80 +0,0 @@ -'use client'; - -import { LanguageIcon } from '@heroicons/react/24/solid'; -import { LanguageCode } from '@socialincome/shared/src/types/Language'; -import { - Accordion, - AccordionContent, - AccordionItem, - AccordionTrigger, - MenubarContent, - MenubarItem, - MenubarMenu, - MenubarTrigger, -} from '@socialincome/ui'; -import { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime'; -import { ReadonlyURLSearchParams, useRouter, useSearchParams } from 'next/navigation'; -import { Suspense } from 'react'; - -type LanguageSwitcherProps = { - mobile?: boolean; - currentLanguage: string; - languages: { - code: LanguageCode; - translation: string; - }[]; -}; - -export function onLanguageChange(lang: LanguageCode, router: AppRouterInstance, searchParams: ReadonlyURLSearchParams) { - const pathSegments = window.location.pathname.split('/'); - pathSegments[1] = lang; - const current = new URLSearchParams(Array.from(searchParams.entries())); - router.push(pathSegments.join('/') + '?' + current.toString()); -} - -function LanguageSwitcherComponent({ languages, mobile, currentLanguage }: LanguageSwitcherProps) { - const router = useRouter(); - const searchParams = useSearchParams(); - - if (mobile) { - return ( - - - -
- -
-
- {languages.map((lang, index) => ( - onLanguageChange(lang.code, router, searchParams)}> - {lang.translation} - - ))} -
-
- ); - } else { - return ( - - - - - - {languages.map((lang, index) => ( - onLanguageChange(lang.code, router, searchParams)}> - {lang.translation} - - ))} - - - ); - } -} - -export function LanguageSwitcher(props: LanguageSwitcherProps) { - return ( - - - - ); -} diff --git a/website/src/components/navbar/navbar-client.tsx b/website/src/components/navbar/navbar-client.tsx index ff0b31cdf..0870e4793 100644 --- a/website/src/components/navbar/navbar-client.tsx +++ b/website/src/components/navbar/navbar-client.tsx @@ -1,10 +1,11 @@ 'use client'; -import { DefaultParams } from '@/app/[lang]/[country]'; + +import { I18nDialog } from '@/components/i18n-dialog'; import { SILogo } from '@/components/logos/si-logo'; -import { LanguageSwitcher } from '@/components/navbar/language-switcher'; +import { WebsiteCurrency } from '@/i18n'; import { UserCircleIcon } from '@heroicons/react/24/outline'; -import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/solid'; -import { LanguageCode } from '@socialincome/shared/src/types/Language'; +import { Bars3Icon, GlobeEuropeAfricaIcon, LanguageIcon, XMarkIcon } from '@heroicons/react/24/solid'; +import { LanguageCode } from '@socialincome/shared/src/types/language'; import { Accordion, AccordionContent, @@ -14,25 +15,15 @@ import { Collapsible, CollapsibleContent, CollapsibleTrigger, - Menubar, - MenubarContent, - MenubarItem, - MenubarMenu, - MenubarTrigger, - NavigationMenu, - NavigationMenuContent, - NavigationMenuItem, - NavigationMenuLink, - NavigationMenuList, - NavigationMenuTrigger, + HoverCard, + HoverCardContent, + HoverCardTrigger, Typography, - navigationMenuTriggerStyle, } from '@socialincome/ui'; -import { signOut } from 'firebase/auth'; import _ from 'lodash'; import Link from 'next/link'; import { useState } from 'react'; -import { useAuth, useUser } from 'reactfire'; +import { DefaultParams } from '../../app/[lang]/[region]'; type NavbarSection = { title: string; @@ -48,7 +39,12 @@ type NavbarProps = { backgroundColor?: string; sections: NavbarSection[]; translations: { + language: string; currentLanguage: string; + region: string; + currentRegion: string; + currency: string; + currentCurrency: string; myProfile: string; contactDetails: string; payments: string; @@ -58,108 +54,128 @@ type NavbarProps = { code: LanguageCode; translation: string; }[]; + regions: { + code: string; + translation: string; + }[]; + currencies: { + code: WebsiteCurrency; + translation: string; + }[]; } & DefaultParams; -export function NavbarClient({ lang, country, translations, languages, sections = [] }: NavbarProps) { - const auth = useAuth(); +export function NavbarClient( + { lang, region, translations, languages, regions, currencies, sections = [] }: NavbarProps, +) { const [isOpen, setIsOpen] = useState(false); - const { status: authUserReady, data: authUser } = useUser(); + const i18nDialog = ( + + + + ); return ( - + ); } diff --git a/website/src/components/navbar/navbar.tsx b/website/src/components/navbar/navbar.tsx index 37ac55d9b..769e01514 100644 --- a/website/src/components/navbar/navbar.tsx +++ b/website/src/components/navbar/navbar.tsx @@ -1,92 +1,70 @@ -import { DefaultParams } from '@/app/[lang]/[country]'; +import { CURRENCY_COOKIE, DefaultParams } from '@/app/[lang]/[region]'; import { NavbarClient } from '@/components/navbar/navbar-client'; -import { LanguageCode } from '@socialincome/shared/src/types/Language'; +import { WebsiteLanguage, websiteCurrencies, websiteRegions } from '@/i18n'; import { Translator } from '@socialincome/shared/src/utils/i18n'; +import { cookies } from 'next/headers'; -export type NavbarProps = { - supportedLanguages: LanguageCode[]; -} & DefaultParams; - -export default async function Navbar({ lang, country, supportedLanguages }: NavbarProps) { +export default async function Navbar({ lang, region }: DefaultParams) { const translator = await Translator.getInstance({ language: lang, namespaces: ['common', 'website-common', 'website-me'], }); + const supportedLanguages: WebsiteLanguage[] = ['en', 'de']; + const currency = cookies().get(CURRENCY_COOKIE)?.value.toLowerCase(); return ( ({ code: lang, translation: translator.t(`languages.${lang}`) }))} + languages={supportedLanguages.map((lang) => ({ + code: lang, + translation: translator.t(`languages.${lang}`), + }))} + regions={websiteRegions.map((country) => ({ + code: country, + translation: translator.t(`regions.${country}`), + }))} + currencies={websiteCurrencies.map((currency) => ({ + code: currency, + translation: translator.t(`currencies.${currency}`), + }))} sections={[ { title: translator.t('navigation.our-work'), - links: [ - { - title: translator.t('navigation.our-work'), - href: `/${lang}/${country}/our-work`, - description: translator.t('navigation.our-work-description'), - }, - { - title: translator.t('navigation.how-it-works'), - href: `/${lang}/${country}/our-work#how-it-works`, - description: translator.t('navigation.how-it-works-description'), - }, - { - title: translator.t('navigation.contributors'), - href: `/${lang}/${country}/our-work#contributors`, - description: translator.t('navigation.contributors-description'), - }, - { - title: translator.t('navigation.recipients'), - href: `/${lang}/${country}/our-work#recipients`, - description: translator.t('navigation.recipients-description'), - }, - { - title: translator.t('navigation.whats-next'), - href: `/${lang}/${country}/our-work#whats-next`, - description: translator.t('navigation.whats-next-description'), - }, - ], + href: `/${lang}/${region}/our-work`, }, { title: translator.t('navigation.about-us'), - links: [ - { - title: translator.t('navigation.about-us'), - href: `/${lang}/${country}/about-us`, - description: translator.t('navigation.about-us-description'), - }, - { - title: translator.t('navigation.team'), - href: `/${lang}/${country}/about-us/team`, - description: translator.t('navigation.team-description'), - }, - ], + href: `/${lang}/${region}/about-us`, }, { title: translator.t('navigation.transparency'), links: [ { title: translator.t('navigation.finances'), - href: `/${lang}/${country}/transparency/finances/usd`, + href: `/${lang}/${region}/transparency/finances/${currency}`, description: translator.t('navigation.finances-description'), }, { title: translator.t('navigation.recipient-selection'), - href: `/${lang}/${country}/transparency/recipient-selection`, + href: `/${lang}/${region}/transparency/recipient-selection`, description: translator.t('navigation.recipient-selection-description'), }, { title: translator.t('navigation.faq'), - href: `/${lang}/${country}/faq`, + href: `/${lang}/${region}/faq`, description: translator.t('navigation.faq-description'), }, ], diff --git a/website/src/components/book/book.tsx b/website/src/components/ui/book.tsx similarity index 100% rename from website/src/components/book/book.tsx rename to website/src/components/ui/book.tsx diff --git a/website/src/components/ui/currency-selector.tsx b/website/src/components/ui/currency-selector.tsx new file mode 100644 index 000000000..fa6886830 --- /dev/null +++ b/website/src/components/ui/currency-selector.tsx @@ -0,0 +1,53 @@ +'use client'; + +import { useI18n } from '@/app/providers/i18n-provider-client'; +import { getFlagComponentByCurrency } from '@/components/country-flags'; +import { WebsiteCurrency } from '@/i18n'; +import { + FontSize, + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectTrigger, + SelectValue, + Typography, +} from '@socialincome/ui'; +import React from 'react'; + +type CurrencySelectorProps = { + currencies: WebsiteCurrency[]; + fontSize?: FontSize; +} & React.HTMLAttributes; + +const CurrencySelector = React.forwardRef( + ({ currencies, fontSize = 'md', ...props }: CurrencySelectorProps, ref) => { + const { currency, setCurrency } = useI18n(); + const Flag = getFlagComponentByCurrency(currency); + + return ( +
+ +
+ ); + }, +); + +CurrencySelector.displayName = 'CurrencySelector'; +export { CurrencySelector }; diff --git a/website/src/hooks/useCookieState.ts b/website/src/hooks/useCookieState.ts new file mode 100644 index 000000000..e83f40519 --- /dev/null +++ b/website/src/hooks/useCookieState.ts @@ -0,0 +1,30 @@ +'use client'; + +import Cookies, { CookieAttributes } from 'js-cookie'; +import { useState } from 'react'; + +export const useCookieState = (key: string, initialValue?: T, options?: CookieAttributes) => { + const [value, setValue] = useState(() => { + let value = Cookies.get(key) as T; + if (value) { + return value; + } else if (initialValue) { + Cookies.set(key, initialValue, options); + return initialValue; + } else { + return undefined; + } + }); + + const setCookie = (val: T, options?: CookieAttributes) => { + Cookies.set(key, val, options); + setValue(val); + }; + + const deleteCookie = () => { + setValue(undefined); + Cookies.remove(key); + }; + + return { value, setCookie, deleteCookie }; +}; diff --git a/website/src/hooks/useTranslator.ts b/website/src/hooks/useTranslator.ts index c502f852f..c5ec14d14 100644 --- a/website/src/hooks/useTranslator.ts +++ b/website/src/hooks/useTranslator.ts @@ -1,4 +1,4 @@ -import { LanguageCode } from '@socialincome/shared/src/types/Language'; +import { LanguageCode } from '@socialincome/shared/src/types/language'; import { Translator } from '@socialincome/shared/src/utils/i18n'; import { useState } from 'react'; diff --git a/website/src/i18n.ts b/website/src/i18n.ts index 7a81d6a0e..c0e68e01f 100644 --- a/website/src/i18n.ts +++ b/website/src/i18n.ts @@ -1,60 +1,46 @@ -import { LanguageCode } from '@socialincome/shared/src/types/Language'; +import { LanguageCode } from '@socialincome/shared/src/types/language'; +import { Translator } from '@socialincome/shared/src/utils/i18n'; import langParser from 'accept-language-parser'; -import { NextRequest, NextResponse } from 'next/server'; +import { NextRequest } from 'next/server'; +import { Currency } from '../../shared/src/types/currency'; -export const defaultCountry = 'us'; -export const countries = ['us', 'ch', 'sl']; -export type ValidCountry = (typeof countries)[number]; - -export const defaultLanguage = 'en'; export type WebsiteLanguage = Extract; +export const defaultLanguage: WebsiteLanguage = 'en'; export const websiteLanguages: WebsiteLanguage[] = ['en', 'de', 'kri']; -export const supportedWebsiteLanguages: WebsiteLanguage[] = ['en', 'de']; -const findBestLocale = (request: NextRequest) => { +export type WebsiteRegion = 'int' | 'ch' | 'sl'; +export const defaultRegion: WebsiteRegion = 'int'; +export const websiteRegions: WebsiteRegion[] = ['int', 'ch', 'sl']; + +export type WebsiteCurrency = Extract; +export const defaultCurrency: WebsiteCurrency = 'USD'; +export const websiteCurrencies: WebsiteCurrency[] = ['USD', 'EUR', 'CHF', 'SLE']; + +export const getCurrencyTranslations = (currencies: WebsiteCurrency[], translator: Translator) => + currencies.map((currency) => ({ + code: currency, + translation: translator.t(`currencies.${currency}`), + })); + +export const findBestLocale = (request: NextRequest): { region: WebsiteRegion; language: WebsiteLanguage } => { const options = langParser.parse(request.headers.get('Accept-Language') || 'en'); // TODO: make sure country is supported // TODO: save country/language/currency in cookie if manually updated - const requestCountry = request.geo?.country?.toLowerCase(); - console.log('requestCountry', requestCountry); + const requestCountry = request.geo?.country; const bestOption = options.find( (option) => option.code && option.region && websiteLanguages.includes(option.code as WebsiteLanguage) && - countries.includes(option.region), + websiteRegions.includes(option.region as WebsiteRegion), ); return { - language: bestOption?.code || defaultLanguage, - country: - (websiteLanguages.includes(requestCountry as WebsiteLanguage) && requestCountry) || - bestOption?.region || - defaultCountry, + language: (bestOption?.code as WebsiteLanguage) || defaultLanguage, + region: + (websiteLanguages.includes(requestCountry as WebsiteLanguage) && (requestCountry as WebsiteRegion)) || + (bestOption?.region as WebsiteRegion) || + defaultRegion, }; }; - -export const internationalizationMiddleware = (request: NextRequest) => { - const segments = request.nextUrl.pathname.split('/'); - const detectedLanguage = segments.at(1) ?? ''; - const detectedCountry = segments.at(2) ?? ''; - - const pathnameIsMissingLanguage = !websiteLanguages.includes(detectedLanguage as WebsiteLanguage); - const pathnameIsMissingCountry = !countries.includes(detectedCountry); - - if (pathnameIsMissingCountry || pathnameIsMissingLanguage) { - let { language, country } = findBestLocale(request); - language = pathnameIsMissingLanguage ? language : detectedLanguage; - country = pathnameIsMissingCountry ? country : detectedCountry; - - const url = request.nextUrl.clone(); - url.pathname = - `/${language}/${country}` + - (pathnameIsMissingLanguage && segments.at(1) ? `/${segments.at(1)}` : '') + - (pathnameIsMissingCountry && segments.at(2) ? `/${segments.at(2)}` : '') + - `/${segments.slice(3).join('/')}`; - - return NextResponse.redirect(url); - } -}; diff --git a/website/src/middleware.ts b/website/src/middleware.ts index f21583c20..09c0af6e3 100644 --- a/website/src/middleware.ts +++ b/website/src/middleware.ts @@ -1,5 +1,7 @@ -import { internationalizationMiddleware } from '@/i18n'; +import { CURRENCY_COOKIE } from '@/app/[lang]/[region]'; +import { WebsiteLanguage, WebsiteRegion, findBestLocale, websiteLanguages, websiteRegions } from '@/i18n'; import { NextRequest, NextResponse } from 'next/server'; +import { bestGuessCurrency, isValidCurrency } from '../../shared/src/types/currency'; export const config = { matcher: [ @@ -8,6 +10,46 @@ export const config = { ], }; +export const currencyMiddleware = (request: NextRequest, response: NextResponse) => { + // Checks if a valid currency is set as a cookie, and sets one with the best guess if not. + if (request.cookies.has(CURRENCY_COOKIE) && isValidCurrency(request.cookies.get(CURRENCY_COOKIE)?.value)) + return response; + const region = request.nextUrl.pathname.split('/').at(2); + const currency = bestGuessCurrency(region); + response.cookies.set({ name: CURRENCY_COOKIE, value: currency, path: '/', maxAge: 60 * 60 * 24 * 365 }); // 1 year + return response; +}; + +export const i18nRedirectMiddleware = (request: NextRequest) => { + // Checks if the language and country in the URL are supported, and redirects to the best locale if not. + const segments = request.nextUrl.pathname.split('/'); + const detectedLanguage = segments.at(1) ?? ''; + const detectedCountry = segments.at(2) ?? ''; + + const pathnameIsMissingLanguage = !websiteLanguages.includes(detectedLanguage as WebsiteLanguage); + const pathnameIsMissingCountry = !websiteRegions.includes(detectedCountry as WebsiteRegion); + + if (pathnameIsMissingCountry || pathnameIsMissingLanguage) { + let { language, region } = findBestLocale(request); + language = pathnameIsMissingLanguage ? language : (detectedLanguage as WebsiteLanguage); + region = pathnameIsMissingCountry ? region : (detectedCountry as WebsiteRegion); + + const url = request.nextUrl.clone(); + url.pathname = + `/${language}/${region}` + + (pathnameIsMissingLanguage && segments.at(1) ? `/${segments.at(1)}` : '') + + (pathnameIsMissingCountry && segments.at(2) ? `/${segments.at(2)}` : '') + + `/${segments.slice(3).join('/')}`; + + return NextResponse.redirect(url); + } +}; + export function middleware(request: NextRequest) { - return internationalizationMiddleware(request) || NextResponse.next(); + let response = i18nRedirectMiddleware(request); + if (response) return response; + + response = NextResponse.next(); + response = currencyMiddleware(request, response); + return response; }