final
This commit is contained in:
@@ -9,10 +9,15 @@ import { useI18n } from '@/lib/i18n'
|
||||
interface DropData {
|
||||
id: number
|
||||
item: string
|
||||
description?: string | null
|
||||
size: number
|
||||
fill: number
|
||||
unit: string
|
||||
ppu: number
|
||||
price_chf?: number | null
|
||||
price_eur?: number | null
|
||||
wholesale_price_chf?: number | null
|
||||
wholesale_price_eur?: number | null
|
||||
image_url: string | null
|
||||
images?: string[] // Array of image URLs (up to 4)
|
||||
created_at: string
|
||||
@@ -30,7 +35,7 @@ interface User {
|
||||
}
|
||||
|
||||
export default function Drop() {
|
||||
const { t } = useI18n()
|
||||
const { t, language } = useI18n()
|
||||
const [drop, setDrop] = useState<DropData | null>(null)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [selectedSize, setSelectedSize] = useState(50)
|
||||
@@ -57,9 +62,11 @@ export default function Drop() {
|
||||
const [selectedImageIndex, setSelectedImageIndex] = useState(0)
|
||||
const [shippingFee, setShippingFee] = useState<number | null>(null)
|
||||
const [loadingShippingFee, setLoadingShippingFee] = useState(false)
|
||||
const [currency, setCurrency] = useState<'CHF' | 'EUR'>('EUR') // Default to EUR
|
||||
// Currency is based on language: English (en) → EUR, German (de) → CHF
|
||||
const currency: 'CHF' | 'EUR' = language === 'de' ? 'CHF' : 'EUR'
|
||||
const [referralPoints, setReferralPoints] = useState<number>(0)
|
||||
const [pointsToChf, setPointsToChf] = useState<number>(100)
|
||||
const [pointsToEur, setPointsToEur] = useState<number>(100)
|
||||
const [pointsToChf, setPointsToChf] = useState<number>(100) // Keep for backward compatibility
|
||||
const [pointsToUse, setPointsToUse] = useState<number>(0)
|
||||
const [loadingPoints, setLoadingPoints] = useState(false)
|
||||
|
||||
@@ -117,7 +124,14 @@ export default function Drop() {
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
setReferralPoints(data.referral_points || 0)
|
||||
setPointsToChf(data.points_to_chf || 100)
|
||||
// Use EUR-based setting (preferred), fallback to CHF converted to EUR
|
||||
if (data.points_to_eur) {
|
||||
setPointsToEur(data.points_to_eur)
|
||||
} else {
|
||||
// Convert CHF to EUR (1 CHF ≈ 1.0309 EUR)
|
||||
setPointsToEur((data.points_to_chf || 100) / 1.030927835)
|
||||
}
|
||||
setPointsToChf(data.points_to_chf || 100) // Keep for backward compatibility
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching referral points:', error)
|
||||
@@ -241,10 +255,9 @@ export default function Drop() {
|
||||
if (!drop) return 0
|
||||
// Minimum price is 5 in user's currency
|
||||
// Calculate minimum grams needed for 5 (EUR or CHF)
|
||||
const pricePerGramEur = drop.ppu / 1000
|
||||
const minPriceEur = currency === 'CHF' ? 5 / 0.97 : 5 // Convert min CHF to EUR equivalent
|
||||
// Use the higher price (standard) to ensure minimum is met
|
||||
return Math.ceil(minPriceEur / pricePerGramEur)
|
||||
const pricePerGram = getPricePerGram()
|
||||
const minPrice = 5 // 5 in user's currency
|
||||
return Math.ceil(minPrice / pricePerGram)
|
||||
}
|
||||
|
||||
const handleCustomQuantityChange = (value: string) => {
|
||||
@@ -367,30 +380,46 @@ export default function Drop() {
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
setShippingFee(data.shipping_fee || 40)
|
||||
setCurrency(data.currency || 'EUR')
|
||||
// Currency is now based on language, not geolocation
|
||||
} else {
|
||||
// Default to 40 EUR if fetch fails
|
||||
setShippingFee(40)
|
||||
setCurrency('EUR')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching shipping fee:', error)
|
||||
// Default to 40 EUR on error
|
||||
setShippingFee(40)
|
||||
setCurrency('EUR')
|
||||
} finally {
|
||||
setLoadingShippingFee(false)
|
||||
}
|
||||
}
|
||||
|
||||
// Convert EUR price to user's currency (CHF for Swiss, EUR for others)
|
||||
// Database stores prices in EUR, so we need to convert if user is Swiss
|
||||
const convertPrice = (priceInEur: number): number => {
|
||||
if (currency === 'CHF') {
|
||||
// Convert EUR to CHF (1 EUR ≈ 0.97 CHF)
|
||||
return priceInEur * 0.97
|
||||
// Get price per gram based on user's currency and wholesale status
|
||||
const getPricePerGramFromDrop = (): number => {
|
||||
if (!drop) return 0
|
||||
|
||||
// Use new price fields if available, otherwise fall back to ppu calculation
|
||||
if (isWholesaleUnlocked) {
|
||||
// Wholesale price
|
||||
if (currency === 'CHF' && drop.wholesale_price_chf != null) {
|
||||
return Number(drop.wholesale_price_chf) || 0
|
||||
} else if (currency === 'EUR' && drop.wholesale_price_eur != null) {
|
||||
return Number(drop.wholesale_price_eur) || 0
|
||||
}
|
||||
// Fallback to ppu calculation if new fields not set
|
||||
const pricePerGramEur = Number(drop.ppu) / 1000
|
||||
return currency === 'CHF' ? pricePerGramEur * 0.97 : pricePerGramEur
|
||||
} else {
|
||||
// Regular price
|
||||
if (currency === 'CHF' && drop.price_chf != null) {
|
||||
return Number(drop.price_chf) || 0
|
||||
} else if (currency === 'EUR' && drop.price_eur != null) {
|
||||
return Number(drop.price_eur) || 0
|
||||
}
|
||||
// Fallback to ppu calculation if new fields not set
|
||||
const pricePerGramEur = Number(drop.ppu) / 1000
|
||||
return currency === 'CHF' ? pricePerGramEur * 0.97 : pricePerGramEur
|
||||
}
|
||||
return priceInEur
|
||||
}
|
||||
|
||||
const handleJoinDrop = () => {
|
||||
@@ -470,6 +499,7 @@ export default function Drop() {
|
||||
pay_currency: selectedCurrency, // Selected payment currency
|
||||
buyer_data_id: buyerData.buyer_data_id, // Buyer delivery data ID
|
||||
points_to_use: pointsToUse, // Points to use for discount
|
||||
currency: currency, // Display currency based on language (EUR for en, CHF for de)
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -526,78 +556,107 @@ export default function Drop() {
|
||||
|
||||
const handlePointsToUseChange = (value: string) => {
|
||||
const numValue = parseFloat(value) || 0
|
||||
const maxPoints = Math.min(referralPoints, calculatePriceBeforeDiscount() * pointsToChf)
|
||||
// Calculate max points based on EUR price (universal base)
|
||||
const priceEur = currency === 'CHF' ? calculatePriceBeforeDiscount() / 0.97 : calculatePriceBeforeDiscount()
|
||||
const maxPoints = Math.min(referralPoints, priceEur * pointsToEur)
|
||||
setPointsToUse(Math.max(0, Math.min(numValue, maxPoints)))
|
||||
}
|
||||
|
||||
const calculatePriceBeforeDiscount = () => {
|
||||
if (!drop) return 0
|
||||
const pricePerGramEur = drop.ppu / 1000
|
||||
const priceToUseEur = isWholesaleUnlocked ? pricePerGramEur * 0.76 : pricePerGramEur
|
||||
const priceEur = selectedSize * priceToUseEur
|
||||
return convertPrice(priceEur)
|
||||
const pricePerGram = getPricePerGramFromDrop()
|
||||
return selectedSize * pricePerGram
|
||||
}
|
||||
|
||||
const getMaxDiscountFromPoints = () => {
|
||||
if (pointsToChf === 0) return 0
|
||||
return referralPoints / pointsToChf
|
||||
if (pointsToEur === 0) return 0
|
||||
// Calculate discount in EUR (universal base)
|
||||
const discountEur = referralPoints / pointsToEur
|
||||
// Convert to user's currency for display
|
||||
if (currency === 'CHF') {
|
||||
return discountEur * 0.97 // Convert EUR to CHF
|
||||
} else {
|
||||
return discountEur // Already in EUR
|
||||
}
|
||||
}
|
||||
|
||||
const calculateDiscountFromPoints = () => {
|
||||
if (pointsToUse === 0 || pointsToChf === 0) return 0
|
||||
// Calculate discount in CHF, then convert to user's currency
|
||||
const discountChf = pointsToUse / pointsToChf
|
||||
// Convert CHF discount to user's currency
|
||||
if (pointsToUse === 0 || pointsToEur === 0) return 0
|
||||
// Calculate discount in EUR first (universal base)
|
||||
const discountEur = pointsToUse / pointsToEur
|
||||
// Convert to user's currency
|
||||
if (currency === 'CHF') {
|
||||
return discountChf
|
||||
return discountEur * 0.97 // Convert EUR to CHF
|
||||
} else {
|
||||
// Convert CHF to EUR (1 CHF ≈ 1.03 EUR)
|
||||
return discountChf * 1.03
|
||||
return discountEur // Already in EUR
|
||||
}
|
||||
}
|
||||
|
||||
const calculatePrice = () => {
|
||||
if (!drop) return 0
|
||||
// ppu is stored as integer where 1000 = 1.00 EUR, so divide by 1000 to get actual price in EUR
|
||||
// Assuming ppu is per gram
|
||||
const pricePerGramEur = drop.ppu / 1000
|
||||
const priceToUseEur = isWholesaleUnlocked ? pricePerGramEur * 0.76 : pricePerGramEur
|
||||
const priceEur = selectedSize * priceToUseEur
|
||||
// Convert to user's currency
|
||||
const price = convertPrice(priceEur)
|
||||
const pricePerGram = getPricePerGramFromDrop()
|
||||
const price = selectedSize * pricePerGram
|
||||
// Apply points discount
|
||||
const discount = calculateDiscountFromPoints()
|
||||
return Math.max(0, price - discount)
|
||||
}
|
||||
|
||||
const calculateStandardPrice = () => {
|
||||
const calculateStandardPrice = (): number => {
|
||||
if (!drop) return 0
|
||||
const pricePerGramEur = drop.ppu / 1000
|
||||
const priceEur = selectedSize * pricePerGramEur
|
||||
// Convert to user's currency
|
||||
return convertPrice(priceEur)
|
||||
// Get regular price (not wholesale)
|
||||
let pricePerGram: number
|
||||
if (currency === 'CHF' && drop.price_chf != null) {
|
||||
pricePerGram = Number(drop.price_chf) || 0
|
||||
} else if (currency === 'EUR' && drop.price_eur != null) {
|
||||
pricePerGram = Number(drop.price_eur) || 0
|
||||
} else {
|
||||
// Fallback to ppu calculation
|
||||
const pricePerGramEur = Number(drop.ppu) / 1000
|
||||
pricePerGram = pricePerGramEur * (currency === 'CHF' ? 0.97 : 1)
|
||||
}
|
||||
return selectedSize * pricePerGram
|
||||
}
|
||||
|
||||
const calculateWholesalePrice = () => {
|
||||
const calculateWholesalePrice = (): number => {
|
||||
if (!drop) return 0
|
||||
const pricePerGramEur = drop.ppu / 1000
|
||||
const priceEur = selectedSize * pricePerGramEur * 0.76
|
||||
// Convert to user's currency
|
||||
return convertPrice(priceEur)
|
||||
// Get wholesale price
|
||||
let pricePerGram: number
|
||||
if (currency === 'CHF' && drop.wholesale_price_chf != null) {
|
||||
pricePerGram = Number(drop.wholesale_price_chf) || 0
|
||||
} else if (currency === 'EUR' && drop.wholesale_price_eur != null) {
|
||||
pricePerGram = Number(drop.wholesale_price_eur) || 0
|
||||
} else {
|
||||
// Fallback to ppu calculation
|
||||
const pricePerGramEur = Number(drop.ppu) / 1000
|
||||
pricePerGram = (pricePerGramEur * 0.76) * (currency === 'CHF' ? 0.97 : 1)
|
||||
}
|
||||
return selectedSize * pricePerGram
|
||||
}
|
||||
|
||||
// Get price per gram in user's currency
|
||||
const getPricePerGram = () => {
|
||||
// Get price per gram in user's currency (regular price)
|
||||
const getPricePerGram = (): number => {
|
||||
if (!drop) return 0
|
||||
const pricePerGramEur = drop.ppu / 1000
|
||||
return convertPrice(pricePerGramEur)
|
||||
if (currency === 'CHF' && drop.price_chf != null) {
|
||||
return Number(drop.price_chf) || 0
|
||||
} else if (currency === 'EUR' && drop.price_eur != null) {
|
||||
return Number(drop.price_eur) || 0
|
||||
}
|
||||
// Fallback to ppu calculation
|
||||
const pricePerGramEur = Number(drop.ppu) / 1000
|
||||
return currency === 'CHF' ? pricePerGramEur * 0.97 : pricePerGramEur
|
||||
}
|
||||
|
||||
// Get wholesale price per gram in user's currency
|
||||
const getWholesalePricePerGram = () => {
|
||||
const getWholesalePricePerGram = (): number => {
|
||||
if (!drop) return 0
|
||||
const pricePerGramEur = drop.ppu / 1000
|
||||
return convertPrice(pricePerGramEur * 0.76)
|
||||
if (currency === 'CHF' && drop.wholesale_price_chf != null) {
|
||||
return Number(drop.wholesale_price_chf) || 0
|
||||
} else if (currency === 'EUR' && drop.wholesale_price_eur != null) {
|
||||
return Number(drop.wholesale_price_eur) || 0
|
||||
}
|
||||
// Fallback to ppu calculation
|
||||
const pricePerGramEur = Number(drop.ppu) / 1000
|
||||
return (pricePerGramEur * 0.76) * (currency === 'CHF' ? 0.97 : 1)
|
||||
}
|
||||
|
||||
const getTimeUntilStart = () => {
|
||||
@@ -763,6 +822,17 @@ export default function Drop() {
|
||||
)}
|
||||
<div>
|
||||
<h2>{drop.item}</h2>
|
||||
{drop.description && (
|
||||
<div style={{
|
||||
marginTop: '12px',
|
||||
marginBottom: '16px',
|
||||
color: 'var(--muted)',
|
||||
lineHeight: '1.6',
|
||||
fontSize: '15px'
|
||||
}}>
|
||||
{drop.description}
|
||||
</div>
|
||||
)}
|
||||
<div className="meta">
|
||||
{formatSize(drop.size, drop.unit)} {t('drop.batch')}
|
||||
</div>
|
||||
@@ -1112,7 +1182,16 @@ export default function Drop() {
|
||||
{user && referralPoints > 0 && (
|
||||
<div style={{ marginTop: '24px', marginBottom: '16px' }}>
|
||||
<label style={{ display: 'block', marginBottom: '8px', fontSize: '14px', color: 'var(--muted)' }}>
|
||||
<strong>⭐ {t('drop.useReferralPoints')}</strong>
|
||||
<strong style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
|
||||
<Image
|
||||
src="/icon_ref_points.png"
|
||||
alt="Referral Points"
|
||||
width={18}
|
||||
height={18}
|
||||
style={{ display: 'inline-block', verticalAlign: 'middle' }}
|
||||
/>
|
||||
{t('drop.useReferralPoints')}
|
||||
</strong>
|
||||
<span style={{ marginLeft: '8px', fontSize: '12px', color: 'var(--muted)' }}>
|
||||
({referralPoints.toFixed(0)} {t('drop.available')})
|
||||
</span>
|
||||
@@ -1139,7 +1218,8 @@ export default function Drop() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
const maxPoints = Math.min(referralPoints, calculatePriceBeforeDiscount() * pointsToChf)
|
||||
const priceEur = currency === 'CHF' ? calculatePriceBeforeDiscount() / 0.97 : calculatePriceBeforeDiscount()
|
||||
const maxPoints = Math.min(referralPoints, priceEur * pointsToEur)
|
||||
setPointsToUse(maxPoints)
|
||||
}}
|
||||
style={{
|
||||
@@ -1158,7 +1238,7 @@ export default function Drop() {
|
||||
</div>
|
||||
{pointsToUse > 0 && (
|
||||
<div style={{ marginTop: '8px', fontSize: '12px', color: '#0a7931' }}>
|
||||
{t('drop.pointsDiscount')}: {(pointsToUse / pointsToChf).toFixed(2)} {currency === 'CHF' ? 'CHF' : 'EUR'}
|
||||
{t('drop.pointsDiscount')}: {calculateDiscountFromPoints().toFixed(2)} {currency === 'CHF' ? 'CHF' : 'EUR'}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -1181,8 +1261,15 @@ export default function Drop() {
|
||||
</div>
|
||||
{pointsToUse > 0 && (
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '8px' }}>
|
||||
<span style={{ color: '#0a7931', fontSize: '14px' }}>
|
||||
⭐ {t('drop.pointsDiscount')}:
|
||||
<span style={{ color: '#0a7931', fontSize: '14px', display: 'flex', alignItems: 'center', gap: '6px' }}>
|
||||
<Image
|
||||
src="/icon_ref_points.png"
|
||||
alt="Referral Points"
|
||||
width={16}
|
||||
height={16}
|
||||
style={{ display: 'inline-block', verticalAlign: 'middle' }}
|
||||
/>
|
||||
{t('drop.pointsDiscount')}:
|
||||
</span>
|
||||
<span style={{ fontWeight: 500, fontSize: '14px', color: '#0a7931' }}>
|
||||
-{calculateDiscountFromPoints().toFixed(2)} {currency}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import Image from 'next/image'
|
||||
import AuthModal from './AuthModal'
|
||||
import RedeemPointsModal from './RedeemPointsModal'
|
||||
import LanguageSwitcher from './LanguageSwitcher'
|
||||
@@ -129,8 +130,18 @@ export default function Nav() {
|
||||
fontSize: '14px',
|
||||
marginLeft: '12px',
|
||||
fontWeight: 500,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '6px',
|
||||
}}>
|
||||
⭐ {user.referral_points.toFixed(0)} pts
|
||||
<Image
|
||||
src="/icon_ref_points.png"
|
||||
alt="Referral Points"
|
||||
width={16}
|
||||
height={16}
|
||||
style={{ display: 'inline-block', verticalAlign: 'middle' }}
|
||||
/>
|
||||
{user.referral_points.toFixed(0)} pts
|
||||
</span>
|
||||
<button
|
||||
onClick={() => {
|
||||
@@ -152,7 +163,7 @@ export default function Nav() {
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
Redeem
|
||||
{t('redeemPoints.redeem')}
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import Image from 'next/image'
|
||||
import { useI18n } from '@/lib/i18n'
|
||||
import { ALLOWED_PAYMENT_CURRENCIES } from '@/lib/payment-currencies'
|
||||
|
||||
interface RedeemPointsModalProps {
|
||||
isOpen: boolean
|
||||
@@ -19,7 +19,7 @@ export default function RedeemPointsModal({
|
||||
}: RedeemPointsModalProps) {
|
||||
const { t } = useI18n()
|
||||
const [pointsToRedeem, setPointsToRedeem] = useState<string>('')
|
||||
const [cryptoCurrency, setCryptoCurrency] = useState<string>('btc')
|
||||
const [cryptoCurrency] = useState<string>('usdtsol') // Fixed to USDT (SOL) only
|
||||
const [walletAddress, setWalletAddress] = useState<string>('')
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState<string>('')
|
||||
@@ -35,7 +35,6 @@ export default function RedeemPointsModal({
|
||||
// Reset form
|
||||
setPointsToRedeem('')
|
||||
setWalletAddress('')
|
||||
setCryptoCurrency('btc')
|
||||
setError('')
|
||||
setSuccess(false)
|
||||
setRedemptionDetails(null)
|
||||
@@ -58,25 +57,17 @@ export default function RedeemPointsModal({
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// Calculate estimated crypto amount when points or currency changes
|
||||
// Calculate estimated crypto amount when points changes
|
||||
// USDT (SOL) rate is approximately 0.9 CHF per USDT
|
||||
if (pointsToRedeem && !isNaN(parseFloat(pointsToRedeem))) {
|
||||
const points = parseFloat(pointsToRedeem)
|
||||
const chfValue = points / pointsToCryptoChf
|
||||
// Mock exchange rates (should match API)
|
||||
const mockRates: Record<string, number> = {
|
||||
'btc': 85000,
|
||||
'eth': 2500,
|
||||
'sol': 100,
|
||||
'xrp': 0.6,
|
||||
'bnbbsc': 300,
|
||||
'usdterc20': 0.9,
|
||||
}
|
||||
const rate = mockRates[cryptoCurrency.toLowerCase()] || 1
|
||||
setEstimatedCrypto(chfValue / rate)
|
||||
const usdtRate = 0.9 // CHF per USDT (SOL)
|
||||
setEstimatedCrypto(chfValue / usdtRate)
|
||||
} else {
|
||||
setEstimatedCrypto(0)
|
||||
}
|
||||
}, [pointsToRedeem, cryptoCurrency, pointsToCryptoChf])
|
||||
}, [pointsToRedeem, pointsToCryptoChf])
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
@@ -216,7 +207,7 @@ export default function RedeemPointsModal({
|
||||
<div style={{ marginBottom: '24px' }}>
|
||||
<p><strong>{t('redeemPoints.redemptionId')}:</strong> #{redemptionDetails.redemption_id}</p>
|
||||
<p><strong>{t('redeemPoints.pointsRedeemed')}:</strong> {redemptionDetails.points_redeemed.toFixed(2)}</p>
|
||||
<p><strong>{t('redeemPoints.cryptoAmount')}:</strong> {redemptionDetails.crypto_amount.toFixed(8)} {redemptionDetails.crypto_currency.toUpperCase()}</p>
|
||||
<p><strong>{t('redeemPoints.cryptoAmount')}:</strong> {redemptionDetails.crypto_amount.toFixed(8)} USDT</p>
|
||||
<p><strong>{t('redeemPoints.newBalance')}:</strong> {redemptionDetails.new_balance.toFixed(2)} {t('redeemPoints.points')}</p>
|
||||
<p style={{ marginTop: '16px', fontSize: '14px', color: 'var(--muted)' }}>
|
||||
{redemptionDetails.message}
|
||||
@@ -252,18 +243,23 @@ export default function RedeemPointsModal({
|
||||
<div style={{ fontSize: '14px', color: 'var(--muted)', marginBottom: '4px' }}>
|
||||
{t('redeemPoints.currentBalance')}
|
||||
</div>
|
||||
<div style={{ fontSize: '24px', fontWeight: 600, color: '#0a7931' }}>
|
||||
⭐ {currentPoints.toFixed(2)} {t('redeemPoints.points')}
|
||||
<div style={{ fontSize: '24px', fontWeight: 600, color: '#0a7931', display: 'flex', alignItems: 'center', gap: '8px' }}>
|
||||
<Image
|
||||
src="/icon_ref_points.png"
|
||||
alt="Referral Points"
|
||||
width={24}
|
||||
height={24}
|
||||
style={{ display: 'inline-block', verticalAlign: 'middle' }}
|
||||
/>
|
||||
{currentPoints.toFixed(2)} {t('redeemPoints.points')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label style={{ display: 'block', marginBottom: '8px', fontSize: '14px', fontWeight: 500 }}>
|
||||
{t('redeemPoints.selectCrypto')} *
|
||||
</label>
|
||||
<select
|
||||
value={cryptoCurrency}
|
||||
onChange={(e) => setCryptoCurrency(e.target.value)}
|
||||
style={{
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<label style={{ display: 'block', marginBottom: '8px', fontSize: '14px', fontWeight: 500 }}>
|
||||
{t('redeemPoints.cryptoCurrency')}
|
||||
</label>
|
||||
<div style={{
|
||||
width: '100%',
|
||||
padding: '12px',
|
||||
background: 'var(--bg-soft)',
|
||||
@@ -271,16 +267,10 @@ export default function RedeemPointsModal({
|
||||
borderRadius: '8px',
|
||||
fontSize: '14px',
|
||||
color: 'var(--text)',
|
||||
marginBottom: '16px',
|
||||
}}
|
||||
required
|
||||
>
|
||||
{ALLOWED_PAYMENT_CURRENCIES.map((currency) => (
|
||||
<option key={currency} value={currency}>
|
||||
{currency.toUpperCase()}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
}}>
|
||||
USDT (SOL)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label style={{ display: 'block', marginBottom: '8px', fontSize: '14px', fontWeight: 500 }}>
|
||||
{t('redeemPoints.walletAddress')} *
|
||||
@@ -310,26 +300,44 @@ export default function RedeemPointsModal({
|
||||
({t('redeemPoints.min')}: {minRedemptionPoints})
|
||||
</span>
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
value={pointsToRedeem}
|
||||
onChange={(e) => setPointsToRedeem(e.target.value)}
|
||||
min={minRedemptionPoints}
|
||||
max={currentPoints}
|
||||
step="1"
|
||||
placeholder={minRedemptionPoints.toString()}
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '12px',
|
||||
background: 'var(--bg-soft)',
|
||||
border: '1px solid var(--border)',
|
||||
borderRadius: '8px',
|
||||
fontSize: '14px',
|
||||
color: 'var(--text)',
|
||||
marginBottom: '16px',
|
||||
}}
|
||||
required
|
||||
/>
|
||||
<div style={{ display: 'flex', gap: '8px', marginBottom: '16px' }}>
|
||||
<input
|
||||
type="number"
|
||||
value={pointsToRedeem}
|
||||
onChange={(e) => setPointsToRedeem(e.target.value)}
|
||||
min={minRedemptionPoints}
|
||||
max={currentPoints}
|
||||
step="1"
|
||||
placeholder={minRedemptionPoints.toString()}
|
||||
style={{
|
||||
flex: 1,
|
||||
padding: '12px',
|
||||
background: 'var(--bg-soft)',
|
||||
border: '1px solid var(--border)',
|
||||
borderRadius: '8px',
|
||||
fontSize: '14px',
|
||||
color: 'var(--text)',
|
||||
}}
|
||||
required
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setPointsToRedeem(currentPoints.toString())}
|
||||
style={{
|
||||
padding: '12px 20px',
|
||||
background: 'var(--bg-soft)',
|
||||
border: '1px solid var(--border)',
|
||||
borderRadius: '8px',
|
||||
fontSize: '14px',
|
||||
color: 'var(--text)',
|
||||
cursor: 'pointer',
|
||||
fontWeight: 500,
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{t('drop.max')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{pointsNum > 0 && (
|
||||
<div style={{
|
||||
@@ -343,7 +351,7 @@ export default function RedeemPointsModal({
|
||||
<strong>{t('redeemPoints.estimatedValue')}:</strong>
|
||||
</div>
|
||||
<div style={{ color: 'var(--muted)' }}>
|
||||
{chfValue.toFixed(2)} CHF ≈ {estimatedCrypto.toFixed(8)} {cryptoCurrency.toUpperCase()}
|
||||
{chfValue.toFixed(2)} CHF ≈ {estimatedCrypto.toFixed(8)} USDT
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user