lang-option
This commit is contained in:
parent
3d66182b95
commit
fb6d47409f
11
src/App.tsx
11
src/App.tsx
|
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { BrowserRouter, Routes, Route, useLocation } from 'react-router-dom';
|
import { BrowserRouter, Routes, Route, useLocation } from 'react-router-dom';
|
||||||
|
import { LanguageProvider } from './contexts/LanguageContext';
|
||||||
import { ChatButton } from './components/ui/ChatButton';
|
import { ChatButton } from './components/ui/ChatButton';
|
||||||
import { AccountSelectionPage } from './pages/AccountSelectionPage';
|
import { AccountSelectionPage } from './pages/AccountSelectionPage';
|
||||||
import { CheckoutPageRoute } from './pages/CheckoutPageRoute';
|
import { CheckoutPageRoute } from './pages/CheckoutPageRoute';
|
||||||
|
|
@ -30,10 +31,12 @@ const AppRoutes: React.FC = () => {
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<BrowserRouter>
|
<LanguageProvider>
|
||||||
<AppRoutes />
|
<BrowserRouter>
|
||||||
<ChatButton />
|
<AppRoutes />
|
||||||
</BrowserRouter>
|
<ChatButton />
|
||||||
|
</BrowserRouter>
|
||||||
|
</LanguageProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { Input } from '../ui/Input';
|
||||||
import { Card } from '../ui/Card';
|
import { Card } from '../ui/Card';
|
||||||
import { Logo } from '../ui/Logo';
|
import { Logo } from '../ui/Logo';
|
||||||
import { DonationSection } from './DonationSection';
|
import { DonationSection } from './DonationSection';
|
||||||
|
import { useLanguage } from '../../contexts/LanguageContext';
|
||||||
import { FiLock, FiArrowLeft, FiEye, FiEyeOff, FiInfo, FiShield, FiCheck, FiCreditCard } from 'react-icons/fi';
|
import { FiLock, FiArrowLeft, FiEye, FiEyeOff, FiInfo, FiShield, FiCheck, FiCreditCard } from 'react-icons/fi';
|
||||||
|
|
||||||
interface CheckoutFormData {
|
interface CheckoutFormData {
|
||||||
|
|
@ -44,6 +45,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
|
||||||
const [referralCode, setReferralCode] = useState('');
|
const [referralCode, setReferralCode] = useState('');
|
||||||
const [paymentMethod, setPaymentMethod] = useState<'card' | 'googlepay' | 'applepay'>('card');
|
const [paymentMethod, setPaymentMethod] = useState<'card' | 'googlepay' | 'applepay'>('card');
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const { t } = useLanguage();
|
||||||
|
|
||||||
const [isProcessing, setIsProcessing] = useState(false);
|
const [isProcessing, setIsProcessing] = useState(false);
|
||||||
|
|
||||||
|
|
@ -189,7 +191,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
|
||||||
.{((total % 1) * 100).toFixed(0).padStart(2, '0')}
|
.{((total % 1) * 100).toFixed(0).padStart(2, '0')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-primary-100 text-sm">Total amount</p>
|
<p className="text-primary-100 text-sm">{t('checkout.totalAmount')}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Selected Plan/Transaction Box */}
|
{/* Selected Plan/Transaction Box */}
|
||||||
|
|
@ -213,20 +215,20 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
|
||||||
|
|
||||||
{/* Order Summary */}
|
{/* Order Summary */}
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<h3 className="text-lg font-semibold text-white mb-4">Order Summary</h3>
|
<h3 className="text-lg font-semibold text-white mb-4">{t('checkout.orderSummary')}</h3>
|
||||||
|
|
||||||
<div className="space-y-3 mb-4">
|
<div className="space-y-3 mb-4">
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-primary-100">Subtotal</span>
|
<span className="text-primary-100">{t('common.subtotal')}</span>
|
||||||
<span className="text-white">{formatCurrency(subtotal)}</span>
|
<span className="text-white">{formatCurrency(subtotal)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-primary-100">Transaction Fee</span>
|
<span className="text-primary-100">{t('checkout.transactionFee')}</span>
|
||||||
<span className="text-white">{formatCurrency(transactionFee)}</span>
|
<span className="text-white">{formatCurrency(transactionFee)}</span>
|
||||||
</div>
|
</div>
|
||||||
{donationAmount > 0 && (
|
{donationAmount > 0 && (
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-primary-100">Donation</span>
|
<span className="text-primary-100">{t('common.donation')}</span>
|
||||||
<span className="text-white">{formatCurrency(donationAmount)}</span>
|
<span className="text-white">{formatCurrency(donationAmount)}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -235,11 +237,11 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
|
||||||
{/* Referral Code Input */}
|
{/* Referral Code Input */}
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<label className="block text-sm text-primary-100 mb-2">
|
<label className="block text-sm text-primary-100 mb-2">
|
||||||
Referral Code
|
{t('checkout.referralCode')}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Enter referral code"
|
placeholder={t('checkout.enterCode')}
|
||||||
value={referralCode}
|
value={referralCode}
|
||||||
onChange={(e) => setReferralCode(e.target.value)}
|
onChange={(e) => setReferralCode(e.target.value)}
|
||||||
className="w-full px-4 py-2 bg-white/10 border border-white/20 rounded-lg text-white placeholder-primary-200 focus:outline-none focus:ring-2 focus:ring-white/50 focus:border-white/40"
|
className="w-full px-4 py-2 bg-white/10 border border-white/20 rounded-lg text-white placeholder-primary-200 focus:outline-none focus:ring-2 focus:ring-white/50 focus:border-white/40"
|
||||||
|
|
@ -248,7 +250,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
|
||||||
|
|
||||||
{/* Total Due */}
|
{/* Total Due */}
|
||||||
<div className="flex justify-between items-center pt-2">
|
<div className="flex justify-between items-center pt-2">
|
||||||
<span className="text-lg font-bold text-white">Total due today</span>
|
<span className="text-lg font-bold text-white">{t('checkout.totalDue')}</span>
|
||||||
<span className="text-2xl font-bold text-white">
|
<span className="text-2xl font-bold text-white">
|
||||||
{formatCurrency(total)}
|
{formatCurrency(total)}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -262,10 +264,10 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
|
||||||
</p>
|
</p>
|
||||||
<div className="flex gap-4 text-xs">
|
<div className="flex gap-4 text-xs">
|
||||||
<a href="#" className="text-primary-100 hover:text-white underline">
|
<a href="#" className="text-primary-100 hover:text-white underline">
|
||||||
Terms
|
{t('common.terms')}
|
||||||
</a>
|
</a>
|
||||||
<a href="#" className="text-primary-100 hover:text-white underline">
|
<a href="#" className="text-primary-100 hover:text-white underline">
|
||||||
Privacy
|
{t('common.privacy')}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -276,7 +278,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
|
||||||
<div className="bg-gray-50 overflow-y-auto">
|
<div className="bg-gray-50 overflow-y-auto">
|
||||||
<div className="max-w-2xl mx-auto px-4 sm:px-6 lg:px-8 py-8 lg:py-12">
|
<div className="max-w-2xl mx-auto px-4 sm:px-6 lg:px-8 py-8 lg:py-12">
|
||||||
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
|
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
|
||||||
<h2 className="text-2xl font-bold text-gray-900 mb-6">Payment Method</h2>
|
<h2 className="text-2xl font-bold text-gray-900 mb-6">{t('checkout.paymentMethod')}</h2>
|
||||||
|
|
||||||
{/* Payment Method Selector */}
|
{/* Payment Method Selector */}
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3 mb-6">
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3 mb-6">
|
||||||
|
|
@ -293,7 +295,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
|
||||||
<div className="flex flex-col items-center gap-2">
|
<div className="flex flex-col items-center gap-2">
|
||||||
<FiCreditCard className={`w-6 h-6 ${paymentMethod === 'card' ? 'text-primary-500' : 'text-gray-600'}`} />
|
<FiCreditCard className={`w-6 h-6 ${paymentMethod === 'card' ? 'text-primary-500' : 'text-gray-600'}`} />
|
||||||
<span className={`text-sm font-medium ${paymentMethod === 'card' ? 'text-primary-600' : 'text-gray-700'}`}>
|
<span className={`text-sm font-medium ${paymentMethod === 'card' ? 'text-primary-600' : 'text-gray-700'}`}>
|
||||||
Card
|
{t('payment.card')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -316,7 +318,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
|
||||||
<path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/>
|
<path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/>
|
||||||
</svg>
|
</svg>
|
||||||
<span className={`text-sm font-medium ${paymentMethod === 'googlepay' ? 'text-primary-600' : 'text-gray-700'}`}>
|
<span className={`text-sm font-medium ${paymentMethod === 'googlepay' ? 'text-primary-600' : 'text-gray-700'}`}>
|
||||||
Google Pay
|
{t('payment.googlePay')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -336,7 +338,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
|
||||||
<path d="M17.05 20.28c-.98.95-2.05.88-3.08.4-1.09-.5-2.08-.48-3.24 0-1.44.62-2.2.44-3.06-.4C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09l.01-.01zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z"/>
|
<path d="M17.05 20.28c-.98.95-2.05.88-3.08.4-1.09-.5-2.08-.48-3.24 0-1.44.62-2.2.44-3.06-.4C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09l.01-.01zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z"/>
|
||||||
</svg>
|
</svg>
|
||||||
<span className={`text-sm font-medium ${paymentMethod === 'applepay' ? 'text-primary-600' : 'text-gray-700'}`}>
|
<span className={`text-sm font-medium ${paymentMethod === 'applepay' ? 'text-primary-600' : 'text-gray-700'}`}>
|
||||||
Apple Pay
|
{t('payment.applePay')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -348,15 +350,15 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
|
||||||
{/* Email */}
|
{/* Email */}
|
||||||
<div>
|
<div>
|
||||||
<Input
|
<Input
|
||||||
label="Email"
|
label={t('common.email')}
|
||||||
type="email"
|
type="email"
|
||||||
placeholder="Email"
|
placeholder={t('common.email')}
|
||||||
error={errors.email?.message}
|
error={errors.email?.message}
|
||||||
{...register('email', {
|
{...register('email', {
|
||||||
required: 'Email is required',
|
required: t('error.required'),
|
||||||
pattern: {
|
pattern: {
|
||||||
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
|
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
|
||||||
message: 'Invalid email address',
|
message: t('error.invalidEmail'),
|
||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
|
|
@ -365,7 +367,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
|
||||||
{/* Card Details */}
|
{/* Card Details */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
Card details
|
{t('checkout.cardDetails')}
|
||||||
</label>
|
</label>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{/* Card Number */}
|
{/* Card Number */}
|
||||||
|
|
@ -378,7 +380,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
|
||||||
errors.cardNumber ? 'border-red-500' : 'border-gray-300'
|
errors.cardNumber ? 'border-red-500' : 'border-gray-300'
|
||||||
}`}
|
}`}
|
||||||
{...register('cardNumber', {
|
{...register('cardNumber', {
|
||||||
required: 'Card number is required',
|
required: t('error.required'),
|
||||||
onChange: (e) => {
|
onChange: (e) => {
|
||||||
const formatted = formatCardNumber(e.target.value);
|
const formatted = formatCardNumber(e.target.value);
|
||||||
e.target.value = formatted;
|
e.target.value = formatted;
|
||||||
|
|
@ -427,7 +429,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
|
||||||
errors.expiryDate ? 'border-red-500' : 'border-gray-300'
|
errors.expiryDate ? 'border-red-500' : 'border-gray-300'
|
||||||
}`}
|
}`}
|
||||||
{...register('expiryDate', {
|
{...register('expiryDate', {
|
||||||
required: 'Expiry date is required',
|
required: t('error.required'),
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
{errors.expiryDate && (
|
{errors.expiryDate && (
|
||||||
|
|
@ -443,7 +445,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
|
||||||
errors.cvv ? 'border-red-500' : 'border-gray-300'
|
errors.cvv ? 'border-red-500' : 'border-gray-300'
|
||||||
}`}
|
}`}
|
||||||
{...register('cvv', {
|
{...register('cvv', {
|
||||||
required: 'CVC is required',
|
required: t('error.required'),
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<FiLock className="absolute right-3 top-1/2 transform -translate-y-1/2 text-secondary-500" />
|
<FiLock className="absolute right-3 top-1/2 transform -translate-y-1/2 text-secondary-500" />
|
||||||
|
|
@ -458,12 +460,12 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
|
||||||
{/* Name on card */}
|
{/* Name on card */}
|
||||||
<div>
|
<div>
|
||||||
<Input
|
<Input
|
||||||
label="Name on card"
|
label={t('checkout.nameOnCard')}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Name on card"
|
placeholder={t('checkout.nameOnCard')}
|
||||||
error={errors.cardName?.message}
|
error={errors.cardName?.message}
|
||||||
{...register('cardName', {
|
{...register('cardName', {
|
||||||
required: 'Name on card is required',
|
required: t('error.required'),
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -474,12 +476,12 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
|
||||||
{/* Billing Address */}
|
{/* Billing Address */}
|
||||||
<div>
|
<div>
|
||||||
<Input
|
<Input
|
||||||
label="Billing address"
|
label={t('checkout.billingAddress')}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Address"
|
placeholder="Address"
|
||||||
error={errors.address?.message}
|
error={errors.address?.message}
|
||||||
{...register('address', {
|
{...register('address', {
|
||||||
required: 'Address is required',
|
required: t('error.required'),
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -500,7 +502,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
|
||||||
className="mt-1 w-4 h-4 text-primary-600 border-gray-300 rounded focus:ring-primary-500"
|
className="mt-1 w-4 h-4 text-primary-600 border-gray-300 rounded focus:ring-primary-500"
|
||||||
/>
|
/>
|
||||||
<label htmlFor="agreeToTerms" className="text-sm text-gray-700">
|
<label htmlFor="agreeToTerms" className="text-sm text-gray-700">
|
||||||
By confirming your payment, you allow us to charge your card for this and future payments in accordance with terms. You can always cancel your subscription.
|
{t('checkout.agreeTerms')}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
{errors.agreeToTerms && (
|
{errors.agreeToTerms && (
|
||||||
|
|
@ -513,7 +515,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
className="w-full bg-primary-500 hover:bg-primary-600 text-white font-medium py-4 px-6 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
className="w-full bg-primary-500 hover:bg-primary-600 text-white font-medium py-4 px-6 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
{isSubmitting ? 'Processing...' : 'Pay'}
|
{isSubmitting ? t('common.processing') : t('common.pay')}
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
@ -548,7 +550,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
|
||||||
<path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/>
|
<path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/>
|
||||||
<path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/>
|
<path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/>
|
||||||
</svg>
|
</svg>
|
||||||
<span>{isProcessing ? 'Processing...' : 'Pay with Google Pay'}</span>
|
<span>{isProcessing ? t('common.processing') : t('payment.payWithGoogle')}</span>
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
@ -580,7 +582,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
|
||||||
<svg className="w-6 h-6" viewBox="0 0 24 24" fill="currentColor">
|
<svg className="w-6 h-6" viewBox="0 0 24 24" fill="currentColor">
|
||||||
<path d="M17.05 20.28c-.98.95-2.05.88-3.08.4-1.09-.5-2.08-.48-3.24 0-1.44.62-2.2.44-3.06-.4C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09l.01-.01zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z"/>
|
<path d="M17.05 20.28c-.98.95-2.05.88-3.08.4-1.09-.5-2.08-.48-3.24 0-1.44.62-2.2.44-3.06-.4C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09l.01-.01zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z"/>
|
||||||
</svg>
|
</svg>
|
||||||
<span>{isProcessing ? 'Processing...' : 'Pay with Apple Pay'}</span>
|
<span>{isProcessing ? t('common.processing') : t('payment.payWithApple')}</span>
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
@ -599,12 +601,12 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="agreeToTermsAlt"
|
id="agreeToTermsAlt"
|
||||||
{...register('agreeToTerms', {
|
{...register('agreeToTerms', {
|
||||||
required: 'You must agree to the terms and conditions',
|
required: t('error.required'),
|
||||||
})}
|
})}
|
||||||
className="mt-1 w-4 h-4 text-primary-600 border-gray-300 rounded focus:ring-primary-500"
|
className="mt-1 w-4 h-4 text-primary-600 border-gray-300 rounded focus:ring-primary-500"
|
||||||
/>
|
/>
|
||||||
<label htmlFor="agreeToTermsAlt" className="text-sm text-gray-700">
|
<label htmlFor="agreeToTermsAlt" className="text-sm text-gray-700">
|
||||||
By confirming your payment, you allow us to charge your card for this and future payments in accordance with terms. You can always cancel your subscription.
|
{t('checkout.agreeTerms')}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
{errors.agreeToTerms && (
|
{errors.agreeToTerms && (
|
||||||
|
|
@ -617,7 +619,7 @@ export const CheckoutPage: React.FC<CheckoutPageProps> = ({
|
||||||
<div className="flex items-center justify-center gap-2 pt-4 border-t border-gray-200">
|
<div className="flex items-center justify-center gap-2 pt-4 border-t border-gray-200">
|
||||||
<div className="flex items-center gap-2 text-sm text-gray-600">
|
<div className="flex items-center gap-2 text-sm text-gray-600">
|
||||||
<FiShield className="w-5 h-5 text-secondary-500" />
|
<FiShield className="w-5 h-5 text-secondary-500" />
|
||||||
<span className="font-medium">Verified checkout page by</span>
|
<span className="font-medium">{t('checkout.verifiedBy')}</span>
|
||||||
<span className="font-bold text-primary-500">AmbaPay</span>
|
<span className="font-bold text-primary-500">AmbaPay</span>
|
||||||
<FiCheck className="w-4 h-4 text-secondary-500" />
|
<FiCheck className="w-4 h-4 text-secondary-500" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import React, { useState, useRef, useEffect } from "react";
|
import React, { useState, useRef, useEffect } from "react";
|
||||||
|
import { useLanguage } from "../../contexts/LanguageContext";
|
||||||
import { FiHeart, FiX, FiChevronLeft, FiChevronRight } from "react-icons/fi";
|
import { FiHeart, FiX, FiChevronLeft, FiChevronRight } from "react-icons/fi";
|
||||||
|
|
||||||
interface DonationCause {
|
interface DonationCause {
|
||||||
|
|
@ -43,6 +44,7 @@ const mockCauses: DonationCause[] = [
|
||||||
export const DonationSection: React.FC<DonationSectionProps> = ({
|
export const DonationSection: React.FC<DonationSectionProps> = ({
|
||||||
onDonationChange,
|
onDonationChange,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { t } = useLanguage();
|
||||||
const [donationAmount, setDonationAmount] = useState("");
|
const [donationAmount, setDonationAmount] = useState("");
|
||||||
const [selectedCause, setSelectedCause] = useState<DonationCause | null>(
|
const [selectedCause, setSelectedCause] = useState<DonationCause | null>(
|
||||||
null
|
null
|
||||||
|
|
@ -111,7 +113,12 @@ export const DonationSection: React.FC<DonationSectionProps> = ({
|
||||||
|
|
||||||
// Add "None" option to causes
|
// Add "None" option to causes
|
||||||
const allCauses = [
|
const allCauses = [
|
||||||
{ id: "none", name: "None", description: "No donation", category: "None" },
|
{
|
||||||
|
id: "none",
|
||||||
|
name: t("donation.none"),
|
||||||
|
description: t("donation.noDonation"),
|
||||||
|
category: "None",
|
||||||
|
},
|
||||||
...mockCauses,
|
...mockCauses,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -119,7 +126,7 @@ export const DonationSection: React.FC<DonationSectionProps> = ({
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
Add donation to institution
|
{t("donation.addDonation")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
|
|
@ -134,7 +141,7 @@ export const DonationSection: React.FC<DonationSectionProps> = ({
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
Select Cause
|
{t("donation.selectCause")}
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
{/* Left Scroll Button */}
|
{/* Left Scroll Button */}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Logo } from '../ui/Logo';
|
import { Logo } from '../ui/Logo';
|
||||||
|
import { useLanguage } from '../../contexts/LanguageContext';
|
||||||
import { FiFacebook, FiTwitter, FiInstagram, FiLinkedin, FiSmartphone } from 'react-icons/fi';
|
import { FiFacebook, FiTwitter, FiInstagram, FiLinkedin, FiSmartphone } from 'react-icons/fi';
|
||||||
|
|
||||||
export const PromotionalPanel: React.FC = () => {
|
export const PromotionalPanel: React.FC = () => {
|
||||||
|
const { t } = useLanguage();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-primary-500 h-full flex flex-col justify-between p-8 md:p-12 text-white relative">
|
<div className="bg-primary-500 h-full flex flex-col justify-between p-8 md:p-12 text-white relative">
|
||||||
<div
|
<div
|
||||||
|
|
@ -17,30 +20,30 @@ export const PromotionalPanel: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 flex flex-col justify-center items-center text-center">
|
<div className="flex-1 flex flex-col justify-center items-center text-center">
|
||||||
<h1 className="text-4xl md:text-5xl lg:text-6xl xl:text-7xl font-bold mb-6 max-w-2xl">
|
<h1 className="text-4xl md:text-5xl lg:text-6xl xl:text-7xl font-bold mb-6 max-w-2xl">
|
||||||
Start searching for homes or posting ads now.
|
{t('promo.title')}
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-primary-100 text-base md:text-lg max-w-xl">
|
<p className="text-primary-100 text-base md:text-lg max-w-xl">
|
||||||
Interfaces are well designed for all ages and target audiences are extremely simple and work with social media integrations
|
{t('promo.description')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-auto">
|
<div className="mt-auto">
|
||||||
{/* Download App Buttons */}
|
{/* Download App Buttons */}
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<p className="text-sm text-primary-100 text-center mb-3">Download the Amba App</p>
|
<p className="text-sm text-primary-100 text-center mb-3">{t('promo.downloadApp')}</p>
|
||||||
<div className="flex gap-3 justify-center">
|
<div className="flex gap-3 justify-center">
|
||||||
<button
|
<button
|
||||||
onClick={() => window.open('https://apps.apple.com/app/amba', '_blank')}
|
onClick={() => window.open('https://apps.apple.com/app/amba', '_blank')}
|
||||||
className="flex items-center gap-2 px-4 py-2 bg-white/10 hover:bg-white/20 text-white rounded-lg transition-colors border border-white/20"
|
className="flex items-center gap-2 px-4 py-2 bg-white/10 hover:bg-white/20 text-white rounded-lg transition-colors border border-white/20"
|
||||||
>
|
>
|
||||||
<FiSmartphone className="w-4 h-4" />
|
<FiSmartphone className="w-4 h-4" />
|
||||||
<span className="text-sm font-medium">iOS</span>
|
<span className="text-sm font-medium">{t('promo.ios')}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => window.open('https://play.google.com/store/apps/details?id=com.amba', '_blank')}
|
onClick={() => window.open('https://play.google.com/store/apps/details?id=com.amba', '_blank')}
|
||||||
className="flex items-center gap-2 px-4 py-2 bg-white/10 hover:bg-white/20 text-white rounded-lg transition-colors border border-white/20"
|
className="flex items-center gap-2 px-4 py-2 bg-white/10 hover:bg-white/20 text-white rounded-lg transition-colors border border-white/20"
|
||||||
>
|
>
|
||||||
<FiSmartphone className="w-4 h-4" />
|
<FiSmartphone className="w-4 h-4" />
|
||||||
<span className="text-sm font-medium">Android</span>
|
<span className="text-sm font-medium">{t('promo.android')}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -80,11 +83,11 @@ export const PromotionalPanel: React.FC = () => {
|
||||||
{/* Footer Links */}
|
{/* Footer Links */}
|
||||||
<div className="flex gap-4 justify-center text-sm text-primary-100 pb-4">
|
<div className="flex gap-4 justify-center text-sm text-primary-100 pb-4">
|
||||||
<a href="#" className="hover:text-white underline">
|
<a href="#" className="hover:text-white underline">
|
||||||
Terms
|
{t('common.terms')}
|
||||||
</a>
|
</a>
|
||||||
<span className="text-primary-200">•</span>
|
<span className="text-primary-200">•</span>
|
||||||
<a href="#" className="hover:text-white underline">
|
<a href="#" className="hover:text-white underline">
|
||||||
Privacy Policy
|
{t('common.privacy')}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react";
|
||||||
import { Card } from "../ui/Card";
|
import { Card } from "../ui/Card";
|
||||||
import { PromotionalPanel } from "./PromotionalPanel";
|
import { PromotionalPanel } from "./PromotionalPanel";
|
||||||
import type { RecipientBankAccount } from "../../types";
|
import type { RecipientBankAccount } from "../../types";
|
||||||
|
import { useLanguage, languages } from "../../contexts/LanguageContext";
|
||||||
import {
|
import {
|
||||||
FiBriefcase,
|
FiBriefcase,
|
||||||
FiUser,
|
FiUser,
|
||||||
|
|
@ -38,15 +39,9 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
|
||||||
const [showFlagModal, setShowFlagModal] = useState(false);
|
const [showFlagModal, setShowFlagModal] = useState(false);
|
||||||
const [flagReason, setFlagReason] = useState("");
|
const [flagReason, setFlagReason] = useState("");
|
||||||
const [showLanguageMenu, setShowLanguageMenu] = useState(false);
|
const [showLanguageMenu, setShowLanguageMenu] = useState(false);
|
||||||
const [selectedLanguage, setSelectedLanguage] = useState("English");
|
const { currentLanguage, setLanguage, t } = useLanguage();
|
||||||
|
|
||||||
const languages = [
|
const currentLangName = languages.find(l => l.code === currentLanguage)?.name || 'English';
|
||||||
{ code: "en", name: "English" },
|
|
||||||
{ code: "fr", name: "French" },
|
|
||||||
{ code: "am", name: "Amharic" },
|
|
||||||
{ code: "om", name: "Oromo" },
|
|
||||||
{ code: "ti", name: "Tigrinya" },
|
|
||||||
];
|
|
||||||
const formatCurrency = (amount: number) => {
|
const formatCurrency = (amount: number) => {
|
||||||
return new Intl.NumberFormat("en-US", {
|
return new Intl.NumberFormat("en-US", {
|
||||||
style: "currency",
|
style: "currency",
|
||||||
|
|
@ -75,7 +70,7 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<div className="flex items-center justify-between mb-4 flex-wrap gap-2">
|
<div className="flex items-center justify-between mb-4 flex-wrap gap-2">
|
||||||
<h2 className="text-lg sm:text-xl font-bold text-gray-800 break-words">
|
<h2 className="text-lg sm:text-xl font-bold text-gray-800 break-words">
|
||||||
Payment Request
|
{t('requester.paymentRequest')}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="flex items-center gap-2 flex-shrink-0">
|
<div className="flex items-center gap-2 flex-shrink-0">
|
||||||
{/* Language Selector */}
|
{/* Language Selector */}
|
||||||
|
|
@ -86,7 +81,7 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
|
||||||
>
|
>
|
||||||
<FiGlobe className="w-4 h-4 text-secondary-500" />
|
<FiGlobe className="w-4 h-4 text-secondary-500" />
|
||||||
<span className="font-medium text-sm">
|
<span className="font-medium text-sm">
|
||||||
{selectedLanguage}
|
{currentLangName}
|
||||||
</span>
|
</span>
|
||||||
<FiChevronDown
|
<FiChevronDown
|
||||||
className={`w-4 h-4 transition-transform ${
|
className={`w-4 h-4 transition-transform ${
|
||||||
|
|
@ -106,13 +101,11 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
|
||||||
<button
|
<button
|
||||||
key={lang.code}
|
key={lang.code}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedLanguage(lang.name);
|
setLanguage(lang.code);
|
||||||
setShowLanguageMenu(false);
|
setShowLanguageMenu(false);
|
||||||
// Here you would typically update the app language
|
|
||||||
// For now, we'll just update the selected language
|
|
||||||
}}
|
}}
|
||||||
className={`w-full text-left px-4 py-2 text-sm hover:bg-gray-50 transition-colors ${
|
className={`w-full text-left px-4 py-2 text-sm hover:bg-gray-50 transition-colors ${
|
||||||
selectedLanguage === lang.name
|
currentLanguage === lang.code
|
||||||
? "bg-primary-50 text-primary-600 font-medium"
|
? "bg-primary-50 text-primary-600 font-medium"
|
||||||
: "text-gray-700"
|
: "text-gray-700"
|
||||||
}`}
|
}`}
|
||||||
|
|
@ -132,7 +125,7 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
|
||||||
className="flex items-center gap-2 px-4 py-2 bg-red-50 hover:bg-red-100 text-red-600 rounded-lg transition-colors border border-red-200"
|
className="flex items-center gap-2 px-4 py-2 bg-red-50 hover:bg-red-100 text-red-600 rounded-lg transition-colors border border-red-200"
|
||||||
>
|
>
|
||||||
<FiFlag className="w-4 h-4" />
|
<FiFlag className="w-4 h-4" />
|
||||||
<span className="font-medium">Report</span>
|
<span className="font-medium">{t('common.report')}</span>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -159,7 +152,7 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
|
||||||
</div>
|
</div>
|
||||||
<div className="text-right flex-shrink-0">
|
<div className="text-right flex-shrink-0">
|
||||||
<p className="text-sm text-gray-600 mb-1 whitespace-nowrap">
|
<p className="text-sm text-gray-600 mb-1 whitespace-nowrap">
|
||||||
Requesting
|
{t('requester.requesting')}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xl sm:text-2xl font-bold text-primary-600 whitespace-nowrap">
|
<p className="text-xl sm:text-2xl font-bold text-primary-600 whitespace-nowrap">
|
||||||
{formatCurrency(requester.requestAmount)}
|
{formatCurrency(requester.requestAmount)}
|
||||||
|
|
@ -184,10 +177,10 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
|
||||||
<div>
|
<div>
|
||||||
<div className="text-left mb-6">
|
<div className="text-left mb-6">
|
||||||
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold text-gray-800 mb-2 break-words">
|
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold text-gray-800 mb-2 break-words">
|
||||||
Choose recipient account
|
{t('requester.chooseRecipient')}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-gray-600 text-sm md:text-base break-words">
|
<p className="text-gray-600 text-sm md:text-base break-words">
|
||||||
Select which account to send the money to
|
{t('requester.chooseRecipientDesc')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -237,10 +230,10 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
|
||||||
<div>
|
<div>
|
||||||
<div className="text-left mb-6">
|
<div className="text-left mb-6">
|
||||||
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold text-gray-800 mb-2 break-words">
|
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold text-gray-800 mb-2 break-words">
|
||||||
Recipient Account
|
{t('requester.recipientAccount')}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-gray-600 text-sm md:text-base break-words">
|
<p className="text-gray-600 text-sm md:text-base break-words">
|
||||||
Money will be sent to this account
|
{t('requester.recipientAccountDesc')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -292,7 +285,7 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
|
||||||
}}
|
}}
|
||||||
className="w-full bg-primary-500 hover:bg-primary-600 text-white font-medium py-3 px-6 rounded-lg transition-colors"
|
className="w-full bg-primary-500 hover:bg-primary-600 text-white font-medium py-3 px-6 rounded-lg transition-colors"
|
||||||
>
|
>
|
||||||
Next Step
|
{t('common.next')}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
@ -303,7 +296,7 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
|
||||||
<div className="p-6">
|
<div className="p-6">
|
||||||
<div className="flex justify-between items-start mb-6">
|
<div className="flex justify-between items-start mb-6">
|
||||||
<h2 className="text-2xl font-bold text-gray-900">
|
<h2 className="text-2xl font-bold text-gray-900">
|
||||||
Report User
|
{t('requester.reportUser')}
|
||||||
</h2>
|
</h2>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
@ -318,7 +311,7 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
|
||||||
|
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<p className="text-sm text-gray-600 mb-4">
|
<p className="text-sm text-gray-600 mb-4">
|
||||||
Why are you reporting{" "}
|
{t('requester.reportReason')}{" "}
|
||||||
<span className="font-medium">{requester.name}</span>?
|
<span className="font-medium">{requester.name}</span>?
|
||||||
</p>
|
</p>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
|
|
@ -332,7 +325,7 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
|
||||||
className="w-4 h-4 text-primary-600"
|
className="w-4 h-4 text-primary-600"
|
||||||
/>
|
/>
|
||||||
<span className="text-sm text-gray-900">
|
<span className="text-sm text-gray-900">
|
||||||
Spam or suspicious activity
|
{t('requester.spam')}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<label className="flex items-center gap-3 p-3 border border-gray-200 rounded-lg cursor-pointer hover:bg-gray-50">
|
<label className="flex items-center gap-3 p-3 border border-gray-200 rounded-lg cursor-pointer hover:bg-gray-50">
|
||||||
|
|
@ -345,7 +338,7 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
|
||||||
className="w-4 h-4 text-primary-600"
|
className="w-4 h-4 text-primary-600"
|
||||||
/>
|
/>
|
||||||
<span className="text-sm text-gray-900">
|
<span className="text-sm text-gray-900">
|
||||||
I don't know this person
|
{t('requester.unknown')}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<label className="flex items-center gap-3 p-3 border border-gray-200 rounded-lg cursor-pointer hover:bg-gray-50">
|
<label className="flex items-center gap-3 p-3 border border-gray-200 rounded-lg cursor-pointer hover:bg-gray-50">
|
||||||
|
|
@ -357,7 +350,7 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
|
||||||
onChange={(e) => setFlagReason(e.target.value)}
|
onChange={(e) => setFlagReason(e.target.value)}
|
||||||
className="w-4 h-4 text-primary-600"
|
className="w-4 h-4 text-primary-600"
|
||||||
/>
|
/>
|
||||||
<span className="text-sm text-gray-900">Other</span>
|
<span className="text-sm text-gray-900">{t('requester.other')}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -374,7 +367,7 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
|
||||||
disabled={!flagReason}
|
disabled={!flagReason}
|
||||||
className="flex-1 bg-red-600 hover:bg-red-700 text-white font-medium py-2 px-4 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
className="flex-1 bg-red-600 hover:bg-red-700 text-white font-medium py-2 px-4 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
Report
|
{t('common.report')}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
@ -383,7 +376,7 @@ export const RequesterDetails: React.FC<RequesterDetailsProps> = ({
|
||||||
}}
|
}}
|
||||||
className="flex-1 bg-gray-200 hover:bg-gray-300 text-gray-800 font-medium py-2 px-4 rounded-lg transition-colors"
|
className="flex-1 bg-gray-200 hover:bg-gray-300 text-gray-800 font-medium py-2 px-4 rounded-lg transition-colors"
|
||||||
>
|
>
|
||||||
Cancel
|
{t('common.cancel')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import React, { useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { Card } from '../ui/Card';
|
import { Card } from '../ui/Card';
|
||||||
import { Logo } from '../ui/Logo';
|
import { Logo } from '../ui/Logo';
|
||||||
|
import { useLanguage } from '../../contexts/LanguageContext';
|
||||||
import { FiCheckCircle, FiMail, FiSmartphone, FiDownload } from 'react-icons/fi';
|
import { FiCheckCircle, FiMail, FiSmartphone, FiDownload } from 'react-icons/fi';
|
||||||
|
|
||||||
interface SuccessPageProps {
|
interface SuccessPageProps {
|
||||||
|
|
@ -17,6 +18,7 @@ export const SuccessPage: React.FC<SuccessPageProps> = ({
|
||||||
}) => {
|
}) => {
|
||||||
const [emailSent, setEmailSent] = useState(false);
|
const [emailSent, setEmailSent] = useState(false);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const { t } = useLanguage();
|
||||||
|
|
||||||
const formatCurrency = (amount: number) => {
|
const formatCurrency = (amount: number) => {
|
||||||
return new Intl.NumberFormat('en-US', {
|
return new Intl.NumberFormat('en-US', {
|
||||||
|
|
@ -53,21 +55,21 @@ export const SuccessPage: React.FC<SuccessPageProps> = ({
|
||||||
<FiCheckCircle className="w-12 h-12 text-primary-500" />
|
<FiCheckCircle className="w-12 h-12 text-primary-500" />
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">
|
<h1 className="text-3xl font-bold text-gray-900 mb-2">
|
||||||
Payment Successful!
|
{t('success.title')}
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-gray-600 mb-1">
|
<p className="text-gray-600 mb-1">
|
||||||
Your payment of {formatCurrency(amount)} has been processed successfully.
|
{t('success.message').replace('{amount}', formatCurrency(amount))}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm text-gray-500">Transaction ID: {transactionId}</p>
|
<p className="text-sm text-gray-500">{t('success.transactionId')}: {transactionId}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Download App Section */}
|
{/* Download App Section */}
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<h2 className="text-xl font-bold text-gray-900 mb-4 text-center">
|
<h2 className="text-xl font-bold text-gray-900 mb-4 text-center">
|
||||||
Download the Amba App
|
{t('success.downloadApp')}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-sm text-gray-600 text-center mb-6">
|
<p className="text-sm text-gray-600 text-center mb-6">
|
||||||
Get the Amba app to manage your payments and transactions on the go.
|
{t('success.downloadDesc')}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Download Buttons */}
|
{/* Download Buttons */}
|
||||||
|
|
@ -78,8 +80,8 @@ export const SuccessPage: React.FC<SuccessPageProps> = ({
|
||||||
>
|
>
|
||||||
<FiSmartphone className="w-6 h-6" />
|
<FiSmartphone className="w-6 h-6" />
|
||||||
<div className="text-left">
|
<div className="text-left">
|
||||||
<p className="text-xs">Download on the</p>
|
<p className="text-xs">{t('success.downloadAppStore')}</p>
|
||||||
<p className="font-semibold">App Store</p>
|
<p className="font-semibold">{t('success.appStore')}</p>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
|
@ -88,8 +90,8 @@ export const SuccessPage: React.FC<SuccessPageProps> = ({
|
||||||
>
|
>
|
||||||
<FiSmartphone className="w-6 h-6" />
|
<FiSmartphone className="w-6 h-6" />
|
||||||
<div className="text-left">
|
<div className="text-left">
|
||||||
<p className="text-xs">Get it on</p>
|
<p className="text-xs">{t('success.downloadGooglePlay')}</p>
|
||||||
<p className="font-semibold">Google Play</p>
|
<p className="font-semibold">{t('success.googlePlay')}</p>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -100,7 +102,7 @@ export const SuccessPage: React.FC<SuccessPageProps> = ({
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<FiMail className="w-5 h-5 text-primary-500" />
|
<FiMail className="w-5 h-5 text-primary-500" />
|
||||||
<div>
|
<div>
|
||||||
<p className="font-medium text-gray-900">Send download link via email</p>
|
<p className="font-medium text-gray-900">{t('success.sendEmail')}</p>
|
||||||
<p className="text-sm text-gray-600">{email}</p>
|
<p className="text-sm text-gray-600">{email}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -109,12 +111,12 @@ export const SuccessPage: React.FC<SuccessPageProps> = ({
|
||||||
disabled={emailSent}
|
disabled={emailSent}
|
||||||
className="px-4 py-2 bg-primary-500 hover:bg-primary-600 text-white rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed text-sm font-medium"
|
className="px-4 py-2 bg-primary-500 hover:bg-primary-600 text-white rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed text-sm font-medium"
|
||||||
>
|
>
|
||||||
{emailSent ? 'Sent!' : 'Send Link'}
|
{emailSent ? t('success.sent') : t('success.sendLink')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{emailSent && (
|
{emailSent && (
|
||||||
<div className="bg-green-50 border border-green-200 rounded-lg p-3 text-sm text-green-800">
|
<div className="bg-green-50 border border-green-200 rounded-lg p-3 text-sm text-green-800">
|
||||||
Download link has been sent to your email!
|
{t('success.emailSent')}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -126,14 +128,14 @@ export const SuccessPage: React.FC<SuccessPageProps> = ({
|
||||||
onClick={() => navigate('/')}
|
onClick={() => navigate('/')}
|
||||||
className="flex-1 px-6 py-3 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors font-medium"
|
className="flex-1 px-6 py-3 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors font-medium"
|
||||||
>
|
>
|
||||||
Back to Home
|
{t('success.backHome')}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => window.print()}
|
onClick={() => window.print()}
|
||||||
className="px-6 py-3 bg-primary-500 hover:bg-primary-600 text-white rounded-lg transition-colors font-medium flex items-center gap-2"
|
className="px-6 py-3 bg-primary-500 hover:bg-primary-600 text-white rounded-lg transition-colors font-medium flex items-center gap-2"
|
||||||
>
|
>
|
||||||
<FiDownload className="w-4 h-4" />
|
<FiDownload className="w-4 h-4" />
|
||||||
Download Receipt
|
{t('success.downloadReceipt')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
|
||||||
574
src/contexts/LanguageContext.tsx
Normal file
574
src/contexts/LanguageContext.tsx
Normal file
|
|
@ -0,0 +1,574 @@
|
||||||
|
import React, { createContext, useContext, useState, ReactNode, useEffect } from 'react';
|
||||||
|
|
||||||
|
export type LanguageCode = 'en' | 'fr' | 'am' | 'om' | 'ti';
|
||||||
|
|
||||||
|
interface Language {
|
||||||
|
code: LanguageCode;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const languages: Language[] = [
|
||||||
|
{ code: 'en', name: 'English' },
|
||||||
|
{ code: 'fr', name: 'French' },
|
||||||
|
{ code: 'am', name: 'Amharic' },
|
||||||
|
{ code: 'om', name: 'Oromo' },
|
||||||
|
{ code: 'ti', name: 'Tigrinya' },
|
||||||
|
];
|
||||||
|
|
||||||
|
interface LanguageContextType {
|
||||||
|
currentLanguage: LanguageCode;
|
||||||
|
setLanguage: (code: LanguageCode) => void;
|
||||||
|
t: (key: string) => string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LanguageContext = createContext<LanguageContextType | undefined>(undefined);
|
||||||
|
|
||||||
|
export const LanguageProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
||||||
|
const [currentLanguage, setCurrentLanguageState] = useState<LanguageCode>(() => {
|
||||||
|
// Get language from localStorage or default to English
|
||||||
|
const saved = localStorage.getItem('language') as LanguageCode;
|
||||||
|
return saved && languages.some(l => l.code === saved) ? saved : 'en';
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
localStorage.setItem('language', currentLanguage);
|
||||||
|
}, [currentLanguage]);
|
||||||
|
|
||||||
|
const setLanguage = (code: LanguageCode) => {
|
||||||
|
setCurrentLanguageState(code);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Translation function - will be implemented with actual translations
|
||||||
|
const t = (key: string): string => {
|
||||||
|
const translations = getTranslations(currentLanguage);
|
||||||
|
return translations[key] || key;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LanguageContext.Provider value={{ currentLanguage, setLanguage, t }}>
|
||||||
|
{children}
|
||||||
|
</LanguageContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useLanguage = () => {
|
||||||
|
const context = useContext(LanguageContext);
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error('useLanguage must be used within a LanguageProvider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Translation data
|
||||||
|
const translations: Record<LanguageCode, Record<string, string>> = {
|
||||||
|
en: {
|
||||||
|
// Common
|
||||||
|
'common.next': 'Next Step',
|
||||||
|
'common.back': 'Back',
|
||||||
|
'common.cancel': 'Cancel',
|
||||||
|
'common.submit': 'Submit',
|
||||||
|
'common.loading': 'Loading...',
|
||||||
|
'common.email': 'Email',
|
||||||
|
'common.address': 'Address',
|
||||||
|
'common.name': 'Name',
|
||||||
|
'common.phone': 'Phone',
|
||||||
|
'common.amount': 'Amount',
|
||||||
|
'common.total': 'Total',
|
||||||
|
'common.subtotal': 'Subtotal',
|
||||||
|
'common.fee': 'Fee',
|
||||||
|
'common.donation': 'Donation',
|
||||||
|
'common.terms': 'Terms',
|
||||||
|
'common.privacy': 'Privacy Policy',
|
||||||
|
'common.report': 'Report',
|
||||||
|
'common.select': 'Select',
|
||||||
|
'common.continue': 'Continue',
|
||||||
|
'common.pay': 'Pay',
|
||||||
|
'common.processing': 'Processing...',
|
||||||
|
|
||||||
|
// Requester Details
|
||||||
|
'requester.paymentRequest': 'Payment Request',
|
||||||
|
'requester.chooseRecipient': 'Choose recipient account',
|
||||||
|
'requester.chooseRecipientDesc': 'Select which account to send the money to',
|
||||||
|
'requester.recipientAccount': 'Recipient Account',
|
||||||
|
'requester.recipientAccountDesc': 'Money will be sent to this account',
|
||||||
|
'requester.requesting': 'Requesting',
|
||||||
|
'requester.reportUser': 'Report User',
|
||||||
|
'requester.reportReason': 'Why are you reporting',
|
||||||
|
'requester.spam': 'Spam or suspicious activity',
|
||||||
|
'requester.unknown': "I don't know this person",
|
||||||
|
'requester.other': 'Other',
|
||||||
|
|
||||||
|
// Checkout
|
||||||
|
'checkout.paymentMethod': 'Payment Method',
|
||||||
|
'checkout.paymentTo': 'Payment to',
|
||||||
|
'checkout.completePayment': 'Complete your payment to proceed',
|
||||||
|
'checkout.totalAmount': 'Total amount',
|
||||||
|
'checkout.orderSummary': 'Order Summary',
|
||||||
|
'checkout.transactionFee': 'Transaction Fee',
|
||||||
|
'checkout.totalDue': 'Total due today',
|
||||||
|
'checkout.payWithCard': 'Pay With Card',
|
||||||
|
'checkout.cardDetails': 'Card details',
|
||||||
|
'checkout.cardNumber': 'Card Number',
|
||||||
|
'checkout.nameOnCard': 'Name on card',
|
||||||
|
'checkout.expiryDate': 'Expiry Date',
|
||||||
|
'checkout.cvc': 'CVC',
|
||||||
|
'checkout.billingAddress': 'Billing address',
|
||||||
|
'checkout.agreeTerms': 'By confirming your payment, you allow us to charge your card for this and future payments in accordance with terms. You can always cancel your subscription.',
|
||||||
|
'checkout.verifiedBy': 'Verified checkout page by',
|
||||||
|
'checkout.referralCode': 'Referral Code',
|
||||||
|
'checkout.enterCode': 'Enter code',
|
||||||
|
|
||||||
|
// Payment Methods
|
||||||
|
'payment.card': 'Card',
|
||||||
|
'payment.googlePay': 'Google Pay',
|
||||||
|
'payment.applePay': 'Apple Pay',
|
||||||
|
'payment.payWithGoogle': 'Pay with Google Pay',
|
||||||
|
'payment.payWithApple': 'Pay with Apple Pay',
|
||||||
|
|
||||||
|
// Donation
|
||||||
|
'donation.addDonation': 'Add donation to institution',
|
||||||
|
'donation.selectCause': 'Select Cause',
|
||||||
|
'donation.none': 'None',
|
||||||
|
'donation.noDonation': 'No donation',
|
||||||
|
|
||||||
|
// Success
|
||||||
|
'success.title': 'Payment Successful!',
|
||||||
|
'success.message': 'Your payment of {amount} has been processed successfully.',
|
||||||
|
'success.transactionId': 'Transaction ID',
|
||||||
|
'success.downloadApp': 'Download the Amba App',
|
||||||
|
'success.downloadDesc': 'Get the Amba app to manage your payments and transactions on the go.',
|
||||||
|
'success.downloadAppStore': 'Download on the',
|
||||||
|
'success.downloadGooglePlay': 'Get it on',
|
||||||
|
'success.appStore': 'App Store',
|
||||||
|
'success.googlePlay': 'Google Play',
|
||||||
|
'success.sendEmail': 'Send download link via email',
|
||||||
|
'success.sendLink': 'Send Link',
|
||||||
|
'success.sent': 'Sent!',
|
||||||
|
'success.emailSent': 'Download link has been sent to your email!',
|
||||||
|
'success.backHome': 'Back to Home',
|
||||||
|
'success.downloadReceipt': 'Download Receipt',
|
||||||
|
|
||||||
|
// Promotional Panel
|
||||||
|
'promo.title': 'Start searching for homes or posting ads now.',
|
||||||
|
'promo.description': 'Interfaces are well designed for all ages and target audiences are extremely simple and work with social media integrations',
|
||||||
|
'promo.downloadApp': 'Download the Amba App',
|
||||||
|
'promo.ios': 'iOS',
|
||||||
|
'promo.android': 'Android',
|
||||||
|
|
||||||
|
// Errors
|
||||||
|
'error.required': 'This field is required',
|
||||||
|
'error.invalidEmail': 'Invalid email address',
|
||||||
|
'error.invalidCard': 'Invalid card number',
|
||||||
|
'error.invalidExpiry': 'Invalid expiry date',
|
||||||
|
'error.invalidCvc': 'Invalid CVC',
|
||||||
|
},
|
||||||
|
fr: {
|
||||||
|
// Common
|
||||||
|
'common.next': 'Étape suivante',
|
||||||
|
'common.back': 'Retour',
|
||||||
|
'common.cancel': 'Annuler',
|
||||||
|
'common.submit': 'Soumettre',
|
||||||
|
'common.loading': 'Chargement...',
|
||||||
|
'common.email': 'E-mail',
|
||||||
|
'common.address': 'Adresse',
|
||||||
|
'common.name': 'Nom',
|
||||||
|
'common.phone': 'Téléphone',
|
||||||
|
'common.amount': 'Montant',
|
||||||
|
'common.total': 'Total',
|
||||||
|
'common.subtotal': 'Sous-total',
|
||||||
|
'common.fee': 'Frais',
|
||||||
|
'common.donation': 'Don',
|
||||||
|
'common.terms': 'Conditions',
|
||||||
|
'common.privacy': 'Politique de confidentialité',
|
||||||
|
'common.report': 'Signaler',
|
||||||
|
'common.select': 'Sélectionner',
|
||||||
|
'common.continue': 'Continuer',
|
||||||
|
'common.pay': 'Payer',
|
||||||
|
'common.processing': 'Traitement...',
|
||||||
|
|
||||||
|
// Requester Details
|
||||||
|
'requester.paymentRequest': 'Demande de paiement',
|
||||||
|
'requester.chooseRecipient': 'Choisir le compte du bénéficiaire',
|
||||||
|
'requester.chooseRecipientDesc': 'Sélectionnez le compte vers lequel envoyer l\'argent',
|
||||||
|
'requester.recipientAccount': 'Compte bénéficiaire',
|
||||||
|
'requester.recipientAccountDesc': 'L\'argent sera envoyé à ce compte',
|
||||||
|
'requester.requesting': 'Demande',
|
||||||
|
'requester.reportUser': 'Signaler l\'utilisateur',
|
||||||
|
'requester.reportReason': 'Pourquoi signalez-vous',
|
||||||
|
'requester.spam': 'Spam ou activité suspecte',
|
||||||
|
'requester.unknown': 'Je ne connais pas cette personne',
|
||||||
|
'requester.other': 'Autre',
|
||||||
|
|
||||||
|
// Checkout
|
||||||
|
'checkout.paymentMethod': 'Méthode de paiement',
|
||||||
|
'checkout.paymentTo': 'Paiement à',
|
||||||
|
'checkout.completePayment': 'Complétez votre paiement pour continuer',
|
||||||
|
'checkout.totalAmount': 'Montant total',
|
||||||
|
'checkout.orderSummary': 'Résumé de la commande',
|
||||||
|
'checkout.transactionFee': 'Frais de transaction',
|
||||||
|
'checkout.totalDue': 'Total dû aujourd\'hui',
|
||||||
|
'checkout.payWithCard': 'Payer par carte',
|
||||||
|
'checkout.cardDetails': 'Détails de la carte',
|
||||||
|
'checkout.cardNumber': 'Numéro de carte',
|
||||||
|
'checkout.nameOnCard': 'Nom sur la carte',
|
||||||
|
'checkout.expiryDate': 'Date d\'expiration',
|
||||||
|
'checkout.cvc': 'CVC',
|
||||||
|
'checkout.billingAddress': 'Adresse de facturation',
|
||||||
|
'checkout.agreeTerms': 'En confirmant votre paiement, vous nous autorisez à débiter votre carte pour ce paiement et les paiements futurs conformément aux conditions. Vous pouvez toujours annuler votre abonnement.',
|
||||||
|
'checkout.verifiedBy': 'Page de paiement vérifiée par',
|
||||||
|
'checkout.referralCode': 'Code de parrainage',
|
||||||
|
'checkout.enterCode': 'Entrer le code',
|
||||||
|
|
||||||
|
// Payment Methods
|
||||||
|
'payment.card': 'Carte',
|
||||||
|
'payment.googlePay': 'Google Pay',
|
||||||
|
'payment.applePay': 'Apple Pay',
|
||||||
|
'payment.payWithGoogle': 'Payer avec Google Pay',
|
||||||
|
'payment.payWithApple': 'Payer avec Apple Pay',
|
||||||
|
|
||||||
|
// Donation
|
||||||
|
'donation.addDonation': 'Ajouter un don à l\'institution',
|
||||||
|
'donation.selectCause': 'Sélectionner une cause',
|
||||||
|
'donation.none': 'Aucun',
|
||||||
|
'donation.noDonation': 'Pas de don',
|
||||||
|
|
||||||
|
// Success
|
||||||
|
'success.title': 'Paiement réussi!',
|
||||||
|
'success.message': 'Votre paiement de {amount} a été traité avec succès.',
|
||||||
|
'success.transactionId': 'ID de transaction',
|
||||||
|
'success.downloadApp': 'Télécharger l\'application Amba',
|
||||||
|
'success.downloadDesc': 'Obtenez l\'application Amba pour gérer vos paiements et transactions en déplacement.',
|
||||||
|
'success.downloadAppStore': 'Télécharger sur',
|
||||||
|
'success.downloadGooglePlay': 'Obtenir sur',
|
||||||
|
'success.appStore': 'App Store',
|
||||||
|
'success.googlePlay': 'Google Play',
|
||||||
|
'success.sendEmail': 'Envoyer le lien de téléchargement par e-mail',
|
||||||
|
'success.sendLink': 'Envoyer le lien',
|
||||||
|
'success.sent': 'Envoyé!',
|
||||||
|
'success.emailSent': 'Le lien de téléchargement a été envoyé à votre e-mail!',
|
||||||
|
'success.backHome': 'Retour à l\'accueil',
|
||||||
|
'success.downloadReceipt': 'Télécharger le reçu',
|
||||||
|
|
||||||
|
// Promotional Panel
|
||||||
|
'promo.title': 'Commencez à rechercher des maisons ou à publier des annonces maintenant.',
|
||||||
|
'promo.description': 'Les interfaces sont bien conçues pour tous les âges et les publics cibles sont extrêmement simples et fonctionnent avec des intégrations de médias sociaux',
|
||||||
|
'promo.downloadApp': 'Télécharger l\'application Amba',
|
||||||
|
'promo.ios': 'iOS',
|
||||||
|
'promo.android': 'Android',
|
||||||
|
|
||||||
|
// Errors
|
||||||
|
'error.required': 'Ce champ est requis',
|
||||||
|
'error.invalidEmail': 'Adresse e-mail invalide',
|
||||||
|
'error.invalidCard': 'Numéro de carte invalide',
|
||||||
|
'error.invalidExpiry': 'Date d\'expiration invalide',
|
||||||
|
'error.invalidCvc': 'CVC invalide',
|
||||||
|
},
|
||||||
|
am: {
|
||||||
|
// Common
|
||||||
|
'common.next': 'የሚቀጥለው ደረጃ',
|
||||||
|
'common.back': 'ተመለስ',
|
||||||
|
'common.cancel': 'ተወዳዳሪ',
|
||||||
|
'common.submit': 'ላክ',
|
||||||
|
'common.loading': 'በመጫን ላይ...',
|
||||||
|
'common.email': 'ኢሜይል',
|
||||||
|
'common.address': 'አድራሻ',
|
||||||
|
'common.name': 'ስም',
|
||||||
|
'common.phone': 'ስልክ',
|
||||||
|
'common.amount': 'መጠን',
|
||||||
|
'common.total': 'ጠቅላላ',
|
||||||
|
'common.subtotal': 'ንዑስ ጠቅላላ',
|
||||||
|
'common.fee': 'ክፍያ',
|
||||||
|
'common.donation': 'ልገሳ',
|
||||||
|
'common.terms': 'ውሎች',
|
||||||
|
'common.privacy': 'የግላዊነት ፖሊሲ',
|
||||||
|
'common.report': 'ሪፖርት',
|
||||||
|
'common.select': 'ምረጥ',
|
||||||
|
'common.continue': 'ቀጥል',
|
||||||
|
'common.pay': 'ክፍያ ይፈጽሙ',
|
||||||
|
'common.processing': 'በማቀናበር ላይ...',
|
||||||
|
|
||||||
|
// Requester Details
|
||||||
|
'requester.paymentRequest': 'የክፍያ ጥያቄ',
|
||||||
|
'requester.chooseRecipient': 'የተቀባዩን መለያ ይምረጡ',
|
||||||
|
'requester.chooseRecipientDesc': 'ገንዘቡን ወደ የትኛው መለያ እንደሚላኩ ይምረጡ',
|
||||||
|
'requester.recipientAccount': 'የተቀባይ መለያ',
|
||||||
|
'requester.recipientAccountDesc': 'ገንዘቡ ወደዚህ መለያ ይላካል',
|
||||||
|
'requester.requesting': 'ይጠይቃል',
|
||||||
|
'requester.reportUser': 'ተጠቃሚውን ሪፖርት ያድርጉ',
|
||||||
|
'requester.reportReason': 'ለምን ነው',
|
||||||
|
'requester.spam': 'ስፓም ወይም አጠራጣሪ እንቅስቃሴ',
|
||||||
|
'requester.unknown': 'ይህን ሰው አላውቅም',
|
||||||
|
'requester.other': 'ሌላ',
|
||||||
|
|
||||||
|
// Checkout
|
||||||
|
'checkout.paymentMethod': 'የክፍያ ዘዴ',
|
||||||
|
'checkout.paymentTo': 'ክፍያ ወደ',
|
||||||
|
'checkout.completePayment': 'ክፍያዎን ለመጠናቀቅ ይቀጥሉ',
|
||||||
|
'checkout.totalAmount': 'ጠቅላላ መጠን',
|
||||||
|
'checkout.orderSummary': 'የትዕዛዝ ማጠቃለያ',
|
||||||
|
'checkout.transactionFee': 'የግብይት ክፍያ',
|
||||||
|
'checkout.totalDue': 'ዛሬ የሚከፈለው ጠቅላላ',
|
||||||
|
'checkout.payWithCard': 'በካርድ ይክፈሉ',
|
||||||
|
'checkout.cardDetails': 'የካርድ ዝርዝሮች',
|
||||||
|
'checkout.cardNumber': 'የካርድ ቁጥር',
|
||||||
|
'checkout.nameOnCard': 'በካርድ ላይ ያለ ስም',
|
||||||
|
'checkout.expiryDate': 'የመጨረሻ ቀን',
|
||||||
|
'checkout.cvc': 'CVC',
|
||||||
|
'checkout.billingAddress': 'የክፍያ አድራሻ',
|
||||||
|
'checkout.agreeTerms': 'ክፍያዎን በማረጋገጥ ላይ ካርድዎን ለዚህ እና ለወደፊት ክፍያዎች በውሎች መሠረት እንድንክፈል እንፈቅድልናል። ማስተናገድዎን ሁልጊዜ ማቋረጥ ይችላሉ።',
|
||||||
|
'checkout.verifiedBy': 'የተረጋገጠ የክፍያ ገጽ በ',
|
||||||
|
'checkout.referralCode': 'የማጣቀሻ ኮድ',
|
||||||
|
'checkout.enterCode': 'ኮድ ያስገቡ',
|
||||||
|
|
||||||
|
// Payment Methods
|
||||||
|
'payment.card': 'ካርድ',
|
||||||
|
'payment.googlePay': 'ጉግል ፔይ',
|
||||||
|
'payment.applePay': 'አፕል ፔይ',
|
||||||
|
'payment.payWithGoogle': 'በጉግል ፔይ ይክፈሉ',
|
||||||
|
'payment.payWithApple': 'በአፕል ፔይ ይክፈሉ',
|
||||||
|
|
||||||
|
// Donation
|
||||||
|
'donation.addDonation': 'ለተቋም ልገሳ ያክሉ',
|
||||||
|
'donation.selectCause': 'ምክንያት ይምረጡ',
|
||||||
|
'donation.none': 'ምንም',
|
||||||
|
'donation.noDonation': 'ምንም ልገሳ የለም',
|
||||||
|
|
||||||
|
// Success
|
||||||
|
'success.title': 'ክፍያ በተሳካ ሁኔታ!',
|
||||||
|
'success.message': 'የ{amount} ክፍያዎ በተሳካ ሁኔታ ተከናውኗል።',
|
||||||
|
'success.transactionId': 'የግብይት መለያ',
|
||||||
|
'success.downloadApp': 'የአምባ መተግበሪያ ያውርዱ',
|
||||||
|
'success.downloadDesc': 'ክፍያዎችዎን እና ግብይቶችዎን በመንቀሳቀስ ላይ ለመስተዳደር የአምባ መተግበሪያ ያግኙ።',
|
||||||
|
'success.downloadAppStore': 'በ',
|
||||||
|
'success.downloadGooglePlay': 'ከ',
|
||||||
|
'success.appStore': 'አፕ ስቶር',
|
||||||
|
'success.googlePlay': 'ጉግል ፕሌይ',
|
||||||
|
'success.sendEmail': 'የማውረድ አገናኝ በኢሜይል ይላኩ',
|
||||||
|
'success.sendLink': 'አገናኝ ይላኩ',
|
||||||
|
'success.sent': 'ተልኳል!',
|
||||||
|
'success.emailSent': 'የማውረድ አገናኝ ወደ ኢሜይልዎ ተልኳል!',
|
||||||
|
'success.backHome': 'ወደ መነሻ ተመለስ',
|
||||||
|
'success.downloadReceipt': 'ደረሰኝ ያውርዱ',
|
||||||
|
|
||||||
|
// Promotional Panel
|
||||||
|
'promo.title': 'አሁን ቤቶችን መፈለግ ወይም ማስታወቂያዎችን ማስቀመጥ ይጀምሩ።',
|
||||||
|
'promo.description': 'መገናኛዎች ለሁሉም ዕድሜዎች በደንብ የተነደፉ እና የዓላማ ገጾች በጣም ቀላል ናቸው እና ከማህበራዊ ሚዲያ ውህደቶች ጋር ይሠራሉ',
|
||||||
|
'promo.downloadApp': 'የአምባ መተግበሪያ ያውርዱ',
|
||||||
|
'promo.ios': 'iOS',
|
||||||
|
'promo.android': 'Android',
|
||||||
|
|
||||||
|
// Errors
|
||||||
|
'error.required': 'ይህ መስክ ያስፈልጋል',
|
||||||
|
'error.invalidEmail': 'ልክ ያልሆነ ኢሜይል አድራሻ',
|
||||||
|
'error.invalidCard': 'ልክ ያልሆነ የካርድ ቁጥር',
|
||||||
|
'error.invalidExpiry': 'ልክ ያልሆነ የመጨረሻ ቀን',
|
||||||
|
'error.invalidCvc': 'ልክ ያልሆነ CVC',
|
||||||
|
},
|
||||||
|
om: {
|
||||||
|
// Common
|
||||||
|
'common.next': 'Darbii Itti Aanu',
|
||||||
|
'common.back': 'Deebi\'i',
|
||||||
|
'common.cancel': 'Haqiisi',
|
||||||
|
'common.submit': 'Ergi',
|
||||||
|
'common.loading': 'Bakka bu\'aa jira...',
|
||||||
|
'common.email': 'Imeelii',
|
||||||
|
'common.address': 'Teessoo',
|
||||||
|
'common.name': 'Maqaa',
|
||||||
|
'common.phone': 'Bilbila',
|
||||||
|
'common.amount': 'Hammam',
|
||||||
|
'common.total': 'Waliigala',
|
||||||
|
'common.subtotal': 'Waliigala gabaabaa',
|
||||||
|
'common.fee': 'Kaffaltii',
|
||||||
|
'common.donation': 'Kennaa',
|
||||||
|
'common.terms': 'Haala',
|
||||||
|
'common.privacy': 'Ilaalcha Iccitii',
|
||||||
|
'common.report': 'Odeessi',
|
||||||
|
'common.select': 'Filadhu',
|
||||||
|
'common.continue': 'Itti fufi',
|
||||||
|
'common.pay': 'Kaffalti',
|
||||||
|
'common.processing': 'Odeeffannoo jira...',
|
||||||
|
|
||||||
|
// Requester Details
|
||||||
|
'requester.paymentRequest': 'Gaaffii Kaffaltii',
|
||||||
|
'requester.chooseRecipient': 'Akkaawuntii qeebi\'aa filadhu',
|
||||||
|
'requester.chooseRecipientDesc': 'Akkaawuntii qarshii erguuf filadhu',
|
||||||
|
'requester.recipientAccount': 'Akkaawuntii Qeebi\'aa',
|
||||||
|
'requester.recipientAccountDesc': 'Qarshin kun akkaawuntii kana irratti ergama',
|
||||||
|
'requester.requesting': 'Gaafata',
|
||||||
|
'requester.reportUser': 'Fayyadamaa Odeessi',
|
||||||
|
'requester.reportReason': 'Maaliif',
|
||||||
|
'requester.spam': 'Spam ykn hojii shakkii',
|
||||||
|
'requester.unknown': 'Namni kun hin beeku',
|
||||||
|
'requester.other': 'Kan biraa',
|
||||||
|
|
||||||
|
// Checkout
|
||||||
|
'checkout.paymentMethod': 'Karaa Kaffaltii',
|
||||||
|
'checkout.paymentTo': 'Kaffalti gara',
|
||||||
|
'checkout.completePayment': 'Kaffalti keessan guutaa itti fufaa',
|
||||||
|
'checkout.totalAmount': 'Hammam Waliigala',
|
||||||
|
'checkout.orderSummary': 'Gabaabaa Ajaja',
|
||||||
|
'checkout.transactionFee': 'Kaffaltii Daldalaa',
|
||||||
|
'checkout.totalDue': 'Har\'a Kaffaltamaa',
|
||||||
|
'checkout.payWithCard': 'Kaardii Waliin Kaffalti',
|
||||||
|
'checkout.cardDetails': 'Odeeffannoo Kaardii',
|
||||||
|
'checkout.cardNumber': 'Lakkoofsi Kaardii',
|
||||||
|
'checkout.nameOnCard': 'Maqaan Kaardii Irratti',
|
||||||
|
'checkout.expiryDate': 'Guyyaa Xumuramaa',
|
||||||
|
'checkout.cvc': 'CVC',
|
||||||
|
'checkout.billingAddress': 'Teessoo Kaffaltii',
|
||||||
|
'checkout.agreeTerms': 'Kaffalti keessan mirkaneessanii, kaardii keessan kanaafi kaffaltiwwan kan itti aanuuf haala waliin kaffaltuu nuu dhiheessitan. Abbaandhala keessan yeroo hundumaa dhaamsiisuu ni dandeessan.',
|
||||||
|
'checkout.verifiedBy': 'Fuula kaffaltii mirkaneffame',
|
||||||
|
'checkout.referralCode': 'Koodii Gargaarsa',
|
||||||
|
'checkout.enterCode': 'Koodii Galchi',
|
||||||
|
|
||||||
|
// Payment Methods
|
||||||
|
'payment.card': 'Kaardii',
|
||||||
|
'payment.googlePay': 'Google Pay',
|
||||||
|
'payment.applePay': 'Apple Pay',
|
||||||
|
'payment.payWithGoogle': 'Google Pay Waliin Kaffalti',
|
||||||
|
'payment.payWithApple': 'Apple Pay Waliin Kaffalti',
|
||||||
|
|
||||||
|
// Donation
|
||||||
|
'donation.addDonation': 'Kennaa Hayyamaatti Dabaladhu',
|
||||||
|
'donation.selectCause': 'Sababa Filadhu',
|
||||||
|
'donation.none': 'Hin Qabne',
|
||||||
|
'donation.noDonation': 'Kennaa Hin Qabne',
|
||||||
|
|
||||||
|
// Success
|
||||||
|
'success.title': 'Kaffalti Milkaa\'e!',
|
||||||
|
'success.message': 'Kaffalti {amount} keessan milkaa\'inaan oggaamameera.',
|
||||||
|
'success.transactionId': 'ID Daldalaa',
|
||||||
|
'success.downloadApp': 'App Amba Buufadhu',
|
||||||
|
'success.downloadDesc': 'App Amba argadhu kaffaltiwwan keessaniifi daldalliwwan keessan deemsa keessatti to\'achuuf.',
|
||||||
|
'success.downloadAppStore': 'Buufadhu',
|
||||||
|
'success.downloadGooglePlay': 'Argadhu',
|
||||||
|
'success.appStore': 'App Store',
|
||||||
|
'success.googlePlay': 'Google Play',
|
||||||
|
'success.sendEmail': 'Linki Buufachuu Imeelii Ergi',
|
||||||
|
'success.sendLink': 'Linki Ergi',
|
||||||
|
'success.sent': 'Ergameera!',
|
||||||
|
'success.emailSent': 'Linki buufachuu imeelii keessan irratti ergameera!',
|
||||||
|
'success.backHome': 'Manaa Deebi\'i',
|
||||||
|
'success.downloadReceipt': 'Risiitii Buufadhu',
|
||||||
|
|
||||||
|
// Promotional Panel
|
||||||
|
'promo.title': 'Amma Mana Barbaaduu ykn Ogeessitoota Dhiyeessuu Eegali.',
|
||||||
|
'promo.description': 'Interfaces hundi umurri hundaaaf akka gaariitti ijaaramaniifi bakka maratni hundi baay\'ee salphaadhaafi media hawaasaa waliin hojjata',
|
||||||
|
'promo.downloadApp': 'App Amba Buufadhu',
|
||||||
|
'promo.ios': 'iOS',
|
||||||
|
'promo.android': 'Android',
|
||||||
|
|
||||||
|
// Errors
|
||||||
|
'error.required': 'Bakka kana barbaachisa',
|
||||||
|
'error.invalidEmail': 'Teessoo Imeelii Dogoggora',
|
||||||
|
'error.invalidCard': 'Lakkoofsi Kaardii Dogoggora',
|
||||||
|
'error.invalidExpiry': 'Guyyaa Xumuramaa Dogoggora',
|
||||||
|
'error.invalidCvc': 'CVC Dogoggora',
|
||||||
|
},
|
||||||
|
ti: {
|
||||||
|
// Common
|
||||||
|
'common.next': 'እታሕተዋይ ደረጃ',
|
||||||
|
'common.back': 'ተመለስ',
|
||||||
|
'common.cancel': 'ኣቅርብ',
|
||||||
|
'common.submit': 'ሰዲድ',
|
||||||
|
'common.loading': 'እየተጻወተ...',
|
||||||
|
'common.email': 'ኢ-መይል',
|
||||||
|
'common.address': 'ኣድራሻ',
|
||||||
|
'common.name': 'ስም',
|
||||||
|
'common.phone': 'ተሌፎን',
|
||||||
|
'common.amount': 'መጠን',
|
||||||
|
'common.total': 'ድምር',
|
||||||
|
'common.subtotal': 'ንእሽቶ ድምር',
|
||||||
|
'common.fee': 'ክፍሊት',
|
||||||
|
'common.donation': 'ልግሲ',
|
||||||
|
'common.terms': 'ስምምዕታት',
|
||||||
|
'common.privacy': 'ፖሊሲ ምስጢር',
|
||||||
|
'common.report': 'ሪፖርት',
|
||||||
|
'common.select': 'ምረጥ',
|
||||||
|
'common.continue': 'ቀጽል',
|
||||||
|
'common.pay': 'ክፍሊ',
|
||||||
|
'common.processing': 'እየተሰርሐ...',
|
||||||
|
|
||||||
|
// Requester Details
|
||||||
|
'requester.paymentRequest': 'ሕቶ ክፍሊት',
|
||||||
|
'requester.chooseRecipient': 'ናይ ተቐባሊ ኣካውንት ምረጥ',
|
||||||
|
'requester.chooseRecipientDesc': 'ነዳዲ ናብ ኣየነዳድ ኣካውንት ከም ዝውሃብ ምረጥ',
|
||||||
|
'requester.recipientAccount': 'ኣካውንት ተቐባሊ',
|
||||||
|
'requester.recipientAccountDesc': 'ነዳዲ ናብ እዚ ኣካውንት ክውሃብ እዩ',
|
||||||
|
'requester.requesting': 'ይሕትት',
|
||||||
|
'requester.reportUser': 'ተጠቃሚ ሪፖርት ግበር',
|
||||||
|
'requester.reportReason': 'ንምንታይ',
|
||||||
|
'requester.spam': 'ስፓም ወይ ጐዳይ ንጥፈታት',
|
||||||
|
'requester.unknown': 'እዚ ሰብ ኣይፈልጦን',
|
||||||
|
'requester.other': 'ካልእ',
|
||||||
|
|
||||||
|
// Checkout
|
||||||
|
'checkout.paymentMethod': 'ኣገባብ ክፍሊት',
|
||||||
|
'checkout.paymentTo': 'ክፍሊት ናብ',
|
||||||
|
'checkout.completePayment': 'ክፍሊትካ ንምውዳእ ቀጽል',
|
||||||
|
'checkout.totalAmount': 'ድምር መጠን',
|
||||||
|
'checkout.orderSummary': 'ጽማቝ ትእዛዝ',
|
||||||
|
'checkout.transactionFee': 'ክፍሊት ዕዋፍ',
|
||||||
|
'checkout.totalDue': 'ሎሚ ዝክፈል ድምር',
|
||||||
|
'checkout.payWithCard': 'ብካርድ ክፍሊ',
|
||||||
|
'checkout.cardDetails': 'ዝርዝር ካርድ',
|
||||||
|
'checkout.cardNumber': 'ቁጽሪ ካርድ',
|
||||||
|
'checkout.nameOnCard': 'ስም ኣብ ካርድ',
|
||||||
|
'checkout.expiryDate': 'ዕለት ዝዛዘም',
|
||||||
|
'checkout.cvc': 'CVC',
|
||||||
|
'checkout.billingAddress': 'ኣድራሻ ክፍሊት',
|
||||||
|
'checkout.agreeTerms': 'ክፍሊትካ ብምርግጋጽ ንዚእን ንዝመጽእ ክፍሊትታት ንካርድካ ኣብ ስምምዕታት መሰረት ክንክፍል ንፈቐድና። ምዝገባኻ ኩሉ ግዜ ክትቋርጽ ትኽእል ኢኻ።',
|
||||||
|
'checkout.verifiedBy': 'እተረጋገጸ ገጽ ክፍሊት ብ',
|
||||||
|
'checkout.referralCode': 'ኮድ ምላሽ',
|
||||||
|
'checkout.enterCode': 'ኮድ ኣእቱ',
|
||||||
|
|
||||||
|
// Payment Methods
|
||||||
|
'payment.card': 'ካርድ',
|
||||||
|
'payment.googlePay': 'ጉግል ፔይ',
|
||||||
|
'payment.applePay': 'ኣፕል ፔይ',
|
||||||
|
'payment.payWithGoogle': 'ብጉግል ፔይ ክፍሊ',
|
||||||
|
'payment.payWithApple': 'ብኣፕል ፔይ ክፍሊ',
|
||||||
|
|
||||||
|
// Donation
|
||||||
|
'donation.addDonation': 'ልግሲ ናብ ትካል ወስኽ',
|
||||||
|
'donation.selectCause': 'ምክንያት ምረጥ',
|
||||||
|
'donation.none': 'ኣይኮነን',
|
||||||
|
'donation.noDonation': 'ልግሲ የለን',
|
||||||
|
|
||||||
|
// Success
|
||||||
|
'success.title': 'ክፍሊት ተዓወተ!',
|
||||||
|
'success.message': 'ክፍሊት {amount}ኻ ብዓወት ተፈጺሙ።',
|
||||||
|
'success.transactionId': 'ID ዕዋፍ',
|
||||||
|
'success.downloadApp': 'ኣፕ ኣምባ ኣውርድ',
|
||||||
|
'success.downloadDesc': 'ክፍሊትታትኻን ዕዋፋትኻን ኣብ ምንቅስቓስ ንምቕዳሕ ኣፕ ኣምባ ረኸብ።',
|
||||||
|
'success.downloadAppStore': 'ኣውርድ',
|
||||||
|
'success.downloadGooglePlay': 'ረኸብ',
|
||||||
|
'success.appStore': 'ኣፕ ስቶር',
|
||||||
|
'success.googlePlay': 'ጉግል ፕሌይ',
|
||||||
|
'success.sendEmail': 'ሊንክ ምውራድ ብኢ-መይል ሰዲድ',
|
||||||
|
'success.sendLink': 'ሊንክ ሰዲድ',
|
||||||
|
'success.sent': 'ተሰዲዱ!',
|
||||||
|
'success.emailSent': 'ሊንክ ምውራድ ናብ ኢ-መይልኻ ተሰዲዱ!',
|
||||||
|
'success.backHome': 'ናብ ገዛ ተመለስ',
|
||||||
|
'success.downloadReceipt': 'ረሲት ኣውርድ',
|
||||||
|
|
||||||
|
// Promotional Panel
|
||||||
|
'promo.title': 'ኣሁን ገዛታት ምድላይ ወይ ማስታወሻታት ምስጢር ይጅምሩ።',
|
||||||
|
'promo.description': 'መገናኛታት ንኹሉ ዕድመ ብጽቡቕ ዝተነድፈ እዩ እሞ ዕላማ ሰባት ኣዝዩ ቀሊል እዩ እሞ ምስ ማሕበራዊ ሚድያ ውህደት ይሰርሕ',
|
||||||
|
'promo.downloadApp': 'ኣፕ ኣምባ ኣውርድ',
|
||||||
|
'promo.ios': 'iOS',
|
||||||
|
'promo.android': 'Android',
|
||||||
|
|
||||||
|
// Errors
|
||||||
|
'error.required': 'እዚ ቦታ የድሊ',
|
||||||
|
'error.invalidEmail': 'ዘይቅኑዕ ኢ-መይል ኣድራሻ',
|
||||||
|
'error.invalidCard': 'ዘይቅኑዕ ቁጽሪ ካርድ',
|
||||||
|
'error.invalidExpiry': 'ዘይቅኑዕ ዕለት ዝዛዘም',
|
||||||
|
'error.invalidCvc': 'ዘይቅኑዕ CVC',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function getTranslations(lang: LanguageCode): Record<string, string> {
|
||||||
|
return translations[lang] || translations.en;
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user