feat: 分站前端默认使用人民币显示价格和余额

- 后端 GetDistSiteInfo 返回 currency 配置(符号/汇率/代码)
- SiteContext 新增 useCurrency() hook 提供 symbol/rate/fmt
- Pricing/Dashboard/Topup/Packages 所有价格从 $ 改为 ¥ 并乘以汇率
- 7 个主题的 Home.jsx 套餐价格同步更新

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
abingyydsandClaude Opus 4.6 committed 2026-03-26 17:16:53 +09:00
1 parent 31e4bd22e4
commit c9c711e4f6
12 files changed
+72 -36

No files matched your search

+21
View File
@@ -41,3 +41,24 @@ export function useSite() {
if (!ctx) throw new Error('useSite must be used within SiteProvider'); if (!ctx) throw new Error('useSite must be used within SiteProvider');
return ctx; return ctx;
} }
/**
* useCurrency - 获取分站货币配置
* 返回 { symbol, rate, code, fmt(usdValue) }
* fmt() 将 USD 值转换为显示货币并格式化
*/
export function useCurrency() {
const { site } = useSite();
const currency = site?.currency;
const symbol = currency?.symbol || '¥';
const rate = currency?.exchange_rate || 7;
const code = currency?.code || 'CNY';
const fmt = (usdValue, decimals = 4) => {
if (usdValue == null || isNaN(usdValue)) return '-';
const converted = Number(usdValue) * rate;
return symbol + converted.toFixed(decimals);
};
return { symbol, rate, code, fmt };
}
+5 -3
View File
@@ -3,12 +3,14 @@ import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useAuth } from '../context/AuthContext'; import { useAuth } from '../context/AuthContext';
import { getUserUsage, redeemCode, Q, quotaToDollar } from '../api'; import { getUserUsage, redeemCode, Q, quotaToDollar } from '../api';
import { useCurrency } from '../context/SiteContext';
import CountUp from '../components/bits/CountUp'; import CountUp from '../components/bits/CountUp';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
export default function Dashboard() { export default function Dashboard() {
const { t } = useTranslation(); const { t } = useTranslation();
const { user, refreshUser } = useAuth(); const { user, refreshUser } = useAuth();
const { symbol, rate } = useCurrency();
const [usage, setUsage] = useState(null); const [usage, setUsage] = useState(null);
const [redeemInput, setRedeemInput] = useState(''); const [redeemInput, setRedeemInput] = useState('');
const [redeeming, setRedeeming] = useState(false); const [redeeming, setRedeeming] = useState(false);
@@ -40,7 +42,7 @@ export default function Dashboard() {
const quota = usage?.quota ?? user?.quota ?? 0; const quota = usage?.quota ?? user?.quota ?? 0;
const usedQuota = usage?.used_quota ?? user?.used_quota ?? 0; const usedQuota = usage?.used_quota ?? user?.used_quota ?? 0;
const requestCount = usage?.request_count ?? user?.request_count ?? 0; const requestCount = usage?.request_count ?? user?.request_count ?? 0;
const balanceDollars = quota / Q; const balanceDollars = quota / Q * rate;
return ( return (
<div className="max-w-7xl mx-auto px-6 py-10"> <div className="max-w-7xl mx-auto px-6 py-10">
@@ -57,7 +59,7 @@ export default function Dashboard() {
<div className="glass rounded-2xl p-6"> <div className="glass rounded-2xl p-6">
<p className="text-sm text-page-secondary mb-2">{t('dashboard.balance')}</p> <p className="text-sm text-page-secondary mb-2">{t('dashboard.balance')}</p>
<div className="text-3xl font-bold text-page"> <div className="text-3xl font-bold text-page">
$<CountUp from={0} to={balanceDollars} duration={1.5} /> {symbol}<CountUp from={0} to={balanceDollars} duration={1.5} />
</div> </div>
<p className="text-xs text-page-muted mt-1">{t('dashboard.quotaUnits', { count: quota.toLocaleString() })}</p> <p className="text-xs text-page-muted mt-1">{t('dashboard.quotaUnits', { count: quota.toLocaleString() })}</p>
</div> </div>
@@ -65,7 +67,7 @@ export default function Dashboard() {
<div className="glass rounded-2xl p-6"> <div className="glass rounded-2xl p-6">
<p className="text-sm text-page-secondary mb-2">{t('dashboard.used')}</p> <p className="text-sm text-page-secondary mb-2">{t('dashboard.used')}</p>
<div className="text-3xl font-bold text-page"> <div className="text-3xl font-bold text-page">
$<CountUp from={0} to={usedQuota / Q} duration={1.5} /> {symbol}<CountUp from={0} to={usedQuota / Q * rate} duration={1.5} />
</div> </div>
<p className="text-xs text-page-muted mt-1">{t('dashboard.quotaUnits', { count: usedQuota.toLocaleString() })}</p> <p className="text-xs text-page-muted mt-1">{t('dashboard.quotaUnits', { count: usedQuota.toLocaleString() })}</p>
</div> </div>
+7 -5
View File
@@ -3,6 +3,7 @@ import { Link, useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useAuth } from '../context/AuthContext'; import { useAuth } from '../context/AuthContext';
import { getSitePackages, getSiteModels, subscribePackage, getActiveSubscriptions, Q } from '../api'; import { getSitePackages, getSiteModels, subscribePackage, getActiveSubscriptions, Q } from '../api';
import { useCurrency } from '../context/SiteContext';
import SpotlightCard from '../components/bits/SpotlightCard'; import SpotlightCard from '../components/bits/SpotlightCard';
import { calcOfficialEquivList } from '../utils/officialEquiv'; import { calcOfficialEquivList } from '../utils/officialEquiv';
import RotatingEquiv from '../components/bits/RotatingEquiv'; import RotatingEquiv from '../components/bits/RotatingEquiv';
@@ -24,6 +25,7 @@ export default function Packages() {
const { t } = useTranslation(); const { t } = useTranslation();
const { user, refreshUser } = useAuth(); const { user, refreshUser } = useAuth();
const navigate = useNavigate(); const navigate = useNavigate();
const { symbol, rate } = useCurrency();
const [packages, setPackages] = useState([]); const [packages, setPackages] = useState([]);
const [models, setModels] = useState([]); const [models, setModels] = useState([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
@@ -144,7 +146,7 @@ export default function Packages() {
style={{ width: `${pct}%` }} /> style={{ width: `${pct}%` }} />
</div> </div>
<span className="text-xs text-page-secondary whitespace-nowrap"> <span className="text-xs text-page-secondary whitespace-nowrap">
${(remain / Q).toFixed(2)} / ${(total / Q).toFixed(2)} {symbol}{(remain / Q * rate).toFixed(2)} / {symbol}{(total / Q * rate).toFixed(2)}
</span> </span>
</div> </div>
</div> </div>
@@ -204,9 +206,9 @@ export default function Packages() {
{/* Price */} {/* Price */}
<div className="mb-6"> <div className="mb-6">
<div className="flex items-baseline gap-2"> <div className="flex items-baseline gap-2">
<span className="text-4xl font-bold text-page">${Number(pkg.price).toFixed(2)}</span> <span className="text-4xl font-bold text-page">{symbol}{(Number(pkg.price) * rate).toFixed(2)}</span>
{pkg.original_price > 0 && pkg.original_price > pkg.price && ( {pkg.original_price > 0 && pkg.original_price > pkg.price && (
<span className="text-lg text-page-muted line-through">${Number(pkg.original_price).toFixed(2)}</span> <span className="text-lg text-page-muted line-through">{symbol}{(Number(pkg.original_price) * rate).toFixed(2)}</span>
)} )}
</div> </div>
{pkg.duration > 0 && ( {pkg.duration > 0 && (
@@ -277,7 +279,7 @@ export default function Packages() {
{/* Confirmation Modal */} {/* Confirmation Modal */}
{confirmPkg && (() => { {confirmPkg && (() => {
const userBalance = (user?.quota || 0) / Q; const userBalance = (user?.quota || 0) / Q * rate;
const pkgPrice = Number(confirmPkg.price); const pkgPrice = Number(confirmPkg.price);
const insufficient = userBalance < pkgPrice; const insufficient = userBalance < pkgPrice;
const resetPeriod = confirmPkg.quota_reset_period || 'never'; const resetPeriod = confirmPkg.quota_reset_period || 'never';
@@ -301,7 +303,7 @@ export default function Packages() {
</div> </div>
)} )}
<p className="text-sm text-page-secondary mb-4"> <p className="text-sm text-page-secondary mb-4">
{t('packages.yourBalance')} <span className={`font-medium ${insufficient ? 'text-page-danger' : 'text-page-success'}`}>${userBalance.toFixed(2)}</span> {t('packages.yourBalance')} <span className={`font-medium ${insufficient ? 'text-page-danger' : 'text-page-success'}`}>{symbol}{userBalance.toFixed(2)}</span>
</p> </p>
{insufficient && ( {insufficient && (
<div className="bg-red-500/10 border border-red-500/20 rounded-xl p-3 mb-4"> <div className="bg-red-500/10 border border-red-500/20 rounded-xl p-3 mb-4">
+4 -2
View File
@@ -1,9 +1,11 @@
import React, { useState, useEffect, useMemo } from 'react'; import React, { useState, useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { getSiteModels } from '../api'; import { getSiteModels } from '../api';
import { useCurrency } from '../context/SiteContext';
export default function Pricing() { export default function Pricing() {
const { t } = useTranslation(); const { t } = useTranslation();
const { symbol, rate } = useCurrency();
const [models, setModels] = useState([]); const [models, setModels] = useState([]);
const [vendors, setVendors] = useState([]); const [vendors, setVendors] = useState([]);
const [search, setSearch] = useState(''); const [search, setSearch] = useState('');
@@ -132,10 +134,10 @@ export default function Pricing() {
<span className="font-mono text-page">{m.display_name || m.model_name}</span> <span className="font-mono text-page">{m.display_name || m.model_name}</span>
</td> </td>
<td className="px-5 py-3.5 text-right font-mono text-page-label"> <td className="px-5 py-3.5 text-right font-mono text-page-label">
{m.input_price != null ? `$${(Number(m.input_price) * 1000).toFixed(4)}` : '-'} {m.input_price != null ? `${symbol}${(Number(m.input_price) * 1000 * rate).toFixed(4)}` : '-'}
</td> </td>
<td className="px-5 py-3.5 text-right font-mono text-page-label"> <td className="px-5 py-3.5 text-right font-mono text-page-label">
{m.output_price != null ? `$${(Number(m.output_price) * 1000).toFixed(4)}` : '-'} {m.output_price != null ? `${symbol}${(Number(m.output_price) * 1000 * rate).toFixed(4)}` : '-'}
</td> </td>
<td className="px-5 py-3.5 text-center"> <td className="px-5 py-3.5 text-center">
<span className={`inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-xs border ${ <span className={`inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-xs border ${
+7 -5
View File
@@ -8,6 +8,7 @@ import {
createCryptoOrder, getCryptoOrderStatus, getTopupHistory, createCryptoOrder, getCryptoOrderStatus, getTopupHistory,
Q, quotaToDollar, Q, quotaToDollar,
} from '../api'; } from '../api';
import { useCurrency } from '../context/SiteContext';
import CountUp from '../components/bits/CountUp'; import CountUp from '../components/bits/CountUp';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
@@ -15,6 +16,7 @@ export default function Topup() {
const { t } = useTranslation(); const { t } = useTranslation();
const { user, refreshUser } = useAuth(); const { user, refreshUser } = useAuth();
const { site } = useSite(); const { site } = useSite();
const { symbol, rate } = useCurrency();
const [usage, setUsage] = useState(null); const [usage, setUsage] = useState(null);
const [topupInfo, setTopupInfo] = useState(null); const [topupInfo, setTopupInfo] = useState(null);
@@ -67,7 +69,7 @@ export default function Topup() {
const quota = usage?.quota ?? user?.quota ?? 0; const quota = usage?.quota ?? user?.quota ?? 0;
const usedQuota = usage?.used_quota ?? user?.used_quota ?? 0; const usedQuota = usage?.used_quota ?? user?.used_quota ?? 0;
const requestCount = usage?.request_count ?? user?.request_count ?? 0; const requestCount = usage?.request_count ?? user?.request_count ?? 0;
const balanceDollars = quota / Q; const balanceDollars = quota / Q * rate;
// Redeem // Redeem
const handleRedeem = async (e) => { const handleRedeem = async (e) => {
@@ -324,7 +326,7 @@ export default function Topup() {
<div className="glass rounded-2xl p-6"> <div className="glass rounded-2xl p-6">
<p className="text-sm text-page-secondary mb-2">{t('dashboard.balance')}</p> <p className="text-sm text-page-secondary mb-2">{t('dashboard.balance')}</p>
<div className="text-3xl font-bold text-page"> <div className="text-3xl font-bold text-page">
$<CountUp from={0} to={balanceDollars} duration={1.5} /> {symbol}<CountUp from={0} to={balanceDollars} duration={1.5} />
</div> </div>
<p className="text-xs text-page-muted mt-1">{t('dashboard.quotaUnits', { count: quota.toLocaleString() })}</p> <p className="text-xs text-page-muted mt-1">{t('dashboard.quotaUnits', { count: quota.toLocaleString() })}</p>
</div> </div>
@@ -332,7 +334,7 @@ export default function Topup() {
<div className="glass rounded-2xl p-6"> <div className="glass rounded-2xl p-6">
<p className="text-sm text-page-secondary mb-2">{t('dashboard.used')}</p> <p className="text-sm text-page-secondary mb-2">{t('dashboard.used')}</p>
<div className="text-3xl font-bold text-page"> <div className="text-3xl font-bold text-page">
$<CountUp from={0} to={usedQuota / Q} duration={1.5} /> {symbol}<CountUp from={0} to={usedQuota / Q * rate} duration={1.5} />
</div> </div>
</div> </div>
@@ -371,7 +373,7 @@ export default function Topup() {
: 'glass-sm text-page-label hover:text-page hover:bg-page-surface-hover' : 'glass-sm text-page-label hover:text-page hover:bg-page-surface-hover'
}`} }`}
> >
${val} {symbol}{val}
</button> </button>
))} ))}
</div> </div>
@@ -591,7 +593,7 @@ export default function Topup() {
{history.map((item, i) => ( {history.map((item, i) => (
<div key={i} className="flex items-center justify-between glass-sm rounded-xl px-4 py-3"> <div key={i} className="flex items-center justify-between glass-sm rounded-xl px-4 py-3">
<div> <div>
<p className="text-sm text-page">${item.amount}</p> <p className="text-sm text-page">{symbol}{(Number(item.amount) * rate).toFixed(2)}</p>
<p className="text-xs text-page-muted"> <p className="text-xs text-page-muted">
{new Date(item.create_time * 1000).toLocaleString()} · {item.payment_method || t('topup.redeemCode')} {new Date(item.create_time * 1000).toLocaleString()} · {item.payment_method || t('topup.redeemCode')}
</p> </p>
+4 -3
View File
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useAuth } from '../../context/AuthContext'; import { useAuth } from '../../context/AuthContext';
import { useSite } from '../../context/SiteContext'; import { useSite, useCurrency } from '../../context/SiteContext';
import { getSiteModels, getSitePackages, Q } from '../../api'; import { getSiteModels, getSitePackages, Q } from '../../api';
import { calcOfficialEquivList } from '../../utils/officialEquiv'; import { calcOfficialEquivList } from '../../utils/officialEquiv';
import RotatingEquiv from '../../components/bits/RotatingEquiv'; import RotatingEquiv from '../../components/bits/RotatingEquiv';
@@ -13,6 +13,7 @@ export default function ClaudeHome() {
const { t } = useTranslation(); const { t } = useTranslation();
const { user } = useAuth(); const { user } = useAuth();
const { site } = useSite(); const { site } = useSite();
const { symbol, rate } = useCurrency();
const [models, setModels] = useState([]); const [models, setModels] = useState([]);
const [packages, setPackages] = useState([]); const [packages, setPackages] = useState([]);
@@ -159,9 +160,9 @@ export default function ClaudeHome() {
<h3 className="text-base font-semibold text-[#3D3024]">{pkg.name}</h3> <h3 className="text-base font-semibold text-[#3D3024]">{pkg.name}</h3>
{pkg.description && <p className="text-sm text-[#8B7D6E] mt-1">{pkg.description}</p>} {pkg.description && <p className="text-sm text-[#8B7D6E] mt-1">{pkg.description}</p>}
<div className="mt-auto pt-6"> <div className="mt-auto pt-6">
<span className="text-3xl font-bold text-[#3D3024]">${pkg.price}</span> <span className="text-3xl font-bold text-[#3D3024]">{symbol}{(Number(pkg.price) * rate).toFixed(2)}</span>
{pkg.original_price > pkg.price && ( {pkg.original_price > pkg.price && (
<span className="text-sm text-[#8B7D6E] line-through ml-2">${pkg.original_price}</span> <span className="text-sm text-[#8B7D6E] line-through ml-2">{symbol}{(Number(pkg.original_price) * rate).toFixed(2)}</span>
)} )}
{pkg.duration > 0 && <p className="text-xs text-[#8B7D6E] mt-1">{t('home.days', { count: pkg.duration })}</p>} {pkg.duration > 0 && <p className="text-xs text-[#8B7D6E] mt-1">{t('home.days', { count: pkg.duration })}</p>}
</div> </div>
+4 -3
View File
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useAuth } from '../../context/AuthContext'; import { useAuth } from '../../context/AuthContext';
import { useSite } from '../../context/SiteContext'; import { useSite, useCurrency } from '../../context/SiteContext';
import { getSiteModels, getSitePackages, Q } from '../../api'; import { getSiteModels, getSitePackages, Q } from '../../api';
import { calcOfficialEquivList } from '../../utils/officialEquiv'; import { calcOfficialEquivList } from '../../utils/officialEquiv';
import RotatingEquiv from '../../components/bits/RotatingEquiv'; import RotatingEquiv from '../../components/bits/RotatingEquiv';
@@ -13,6 +13,7 @@ export default function CleanHome() {
const { t } = useTranslation(); const { t } = useTranslation();
const { user } = useAuth(); const { user } = useAuth();
const { site } = useSite(); const { site } = useSite();
const { symbol, rate } = useCurrency();
const [models, setModels] = useState([]); const [models, setModels] = useState([]);
const [packages, setPackages] = useState([]); const [packages, setPackages] = useState([]);
@@ -172,9 +173,9 @@ export default function CleanHome() {
<h3 className="text-base font-semibold text-gray-900">{pkg.name}</h3> <h3 className="text-base font-semibold text-gray-900">{pkg.name}</h3>
{pkg.description && <p className="text-sm text-gray-500 mt-1">{pkg.description}</p>} {pkg.description && <p className="text-sm text-gray-500 mt-1">{pkg.description}</p>}
<div className="mt-auto pt-6"> <div className="mt-auto pt-6">
<span className="text-3xl font-bold text-gray-900">${pkg.price}</span> <span className="text-3xl font-bold text-gray-900">{symbol}{(Number(pkg.price) * rate).toFixed(2)}</span>
{pkg.original_price > pkg.price && ( {pkg.original_price > pkg.price && (
<span className="text-sm text-gray-500 line-through ml-2">${pkg.original_price}</span> <span className="text-sm text-gray-500 line-through ml-2">{symbol}{(Number(pkg.original_price) * rate).toFixed(2)}</span>
)} )}
{pkg.duration > 0 && <p className="text-xs text-gray-500 mt-1">{t('home.days', { count: pkg.duration })}</p>} {pkg.duration > 0 && <p className="text-xs text-gray-500 mt-1">{t('home.days', { count: pkg.duration })}</p>}
</div> </div>
+4 -3
View File
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useAuth } from '../../context/AuthContext'; import { useAuth } from '../../context/AuthContext';
import { useSite } from '../../context/SiteContext'; import { useSite, useCurrency } from '../../context/SiteContext';
import { getSiteModels, getSitePackages, Q } from '../../api'; import { getSiteModels, getSitePackages, Q } from '../../api';
import { calcOfficialEquivList } from '../../utils/officialEquiv'; import { calcOfficialEquivList } from '../../utils/officialEquiv';
import RotatingEquiv from '../../components/bits/RotatingEquiv'; import RotatingEquiv from '../../components/bits/RotatingEquiv';
@@ -13,6 +13,7 @@ export default function CorporateHome() {
const { t } = useTranslation(); const { t } = useTranslation();
const { user } = useAuth(); const { user } = useAuth();
const { site } = useSite(); const { site } = useSite();
const { symbol, rate } = useCurrency();
const [models, setModels] = useState([]); const [models, setModels] = useState([]);
const [packages, setPackages] = useState([]); const [packages, setPackages] = useState([]);
@@ -171,9 +172,9 @@ export default function CorporateHome() {
<h3 className="text-base font-semibold text-slate-900">{pkg.name}</h3> <h3 className="text-base font-semibold text-slate-900">{pkg.name}</h3>
{pkg.description && <p className="text-sm text-slate-500 mt-1">{pkg.description}</p>} {pkg.description && <p className="text-sm text-slate-500 mt-1">{pkg.description}</p>}
<div className="mt-auto pt-6"> <div className="mt-auto pt-6">
<span className="text-3xl font-bold text-slate-900">${pkg.price}</span> <span className="text-3xl font-bold text-slate-900">{symbol}{(Number(pkg.price) * rate).toFixed(2)}</span>
{pkg.original_price > pkg.price && ( {pkg.original_price > pkg.price && (
<span className="text-sm text-slate-500 line-through ml-2">${pkg.original_price}</span> <span className="text-sm text-slate-500 line-through ml-2">{symbol}{(Number(pkg.original_price) * rate).toFixed(2)}</span>
)} )}
{pkg.duration > 0 && <p className="text-xs text-slate-500 mt-1">{t('home.days', { count: pkg.duration })}</p>} {pkg.duration > 0 && <p className="text-xs text-slate-500 mt-1">{t('home.days', { count: pkg.duration })}</p>}
</div> </div>
+4 -3
View File
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useAuth } from '../../context/AuthContext'; import { useAuth } from '../../context/AuthContext';
import { useSite } from '../../context/SiteContext'; import { useSite, useCurrency } from '../../context/SiteContext';
import { getSiteModels, getSitePackages, Q } from '../../api'; import { getSiteModels, getSitePackages, Q } from '../../api';
import { calcOfficialEquivList } from '../../utils/officialEquiv'; import { calcOfficialEquivList } from '../../utils/officialEquiv';
import RotatingEquiv from '../../components/bits/RotatingEquiv'; import RotatingEquiv from '../../components/bits/RotatingEquiv';
@@ -13,6 +13,7 @@ export default function DarkHome() {
const { t } = useTranslation(); const { t } = useTranslation();
const { user } = useAuth(); const { user } = useAuth();
const { site } = useSite(); const { site } = useSite();
const { symbol, rate } = useCurrency();
const [models, setModels] = useState([]); const [models, setModels] = useState([]);
const [packages, setPackages] = useState([]); const [packages, setPackages] = useState([]);
@@ -162,9 +163,9 @@ export default function DarkHome() {
<h3 className="text-base font-semibold text-white">{pkg.name}</h3> <h3 className="text-base font-semibold text-white">{pkg.name}</h3>
{pkg.description && <p className="text-sm text-neutral-500 mt-1">{pkg.description}</p>} {pkg.description && <p className="text-sm text-neutral-500 mt-1">{pkg.description}</p>}
<div className="mt-auto pt-6"> <div className="mt-auto pt-6">
<span className="text-3xl font-bold text-white">${pkg.price}</span> <span className="text-3xl font-bold text-white">{symbol}{(Number(pkg.price) * rate).toFixed(2)}</span>
{pkg.original_price > pkg.price && ( {pkg.original_price > pkg.price && (
<span className="text-sm text-neutral-600 line-through ml-2">${pkg.original_price}</span> <span className="text-sm text-neutral-600 line-through ml-2">{symbol}{(Number(pkg.original_price) * rate).toFixed(2)}</span>
)} )}
{pkg.duration > 0 && <p className="text-xs text-neutral-600 mt-1">{t('home.days', { count: pkg.duration })}</p>} {pkg.duration > 0 && <p className="text-xs text-neutral-600 mt-1">{t('home.days', { count: pkg.duration })}</p>}
</div> </div>
+4 -3
View File
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useAuth } from '../../context/AuthContext'; import { useAuth } from '../../context/AuthContext';
import { useSite } from '../../context/SiteContext'; import { useSite, useCurrency } from '../../context/SiteContext';
import { getSiteModels, getSitePackages, Q } from '../../api'; import { getSiteModels, getSitePackages, Q } from '../../api';
import { calcOfficialEquivList } from '../../utils/officialEquiv'; import { calcOfficialEquivList } from '../../utils/officialEquiv';
import RotatingEquiv from '../../components/bits/RotatingEquiv'; import RotatingEquiv from '../../components/bits/RotatingEquiv';
@@ -13,6 +13,7 @@ export default function DefaultHome() {
const { t } = useTranslation(); const { t } = useTranslation();
const { user } = useAuth(); const { user } = useAuth();
const { site } = useSite(); const { site } = useSite();
const { symbol, rate } = useCurrency();
const [models, setModels] = useState([]); const [models, setModels] = useState([]);
const [packages, setPackages] = useState([]); const [packages, setPackages] = useState([]);
@@ -171,9 +172,9 @@ export default function DefaultHome() {
<h3 className="text-base font-semibold text-white">{pkg.name}</h3> <h3 className="text-base font-semibold text-white">{pkg.name}</h3>
{pkg.description && <p className="text-sm text-neutral-500 mt-1">{pkg.description}</p>} {pkg.description && <p className="text-sm text-neutral-500 mt-1">{pkg.description}</p>}
<div className="mt-auto pt-6"> <div className="mt-auto pt-6">
<span className="text-3xl font-bold text-white">${pkg.price}</span> <span className="text-3xl font-bold text-white">{symbol}{(Number(pkg.price) * rate).toFixed(2)}</span>
{pkg.original_price > pkg.price && ( {pkg.original_price > pkg.price && (
<span className="text-sm text-neutral-600 line-through ml-2">${pkg.original_price}</span> <span className="text-sm text-neutral-600 line-through ml-2">{symbol}{(Number(pkg.original_price) * rate).toFixed(2)}</span>
)} )}
{pkg.duration > 0 && <p className="text-xs text-neutral-600 mt-1">{t('home.days', { count: pkg.duration })}</p>} {pkg.duration > 0 && <p className="text-xs text-neutral-600 mt-1">{t('home.days', { count: pkg.duration })}</p>}
</div> </div>
+4 -3
View File
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useAuth } from '../../context/AuthContext'; import { useAuth } from '../../context/AuthContext';
import { useSite } from '../../context/SiteContext'; import { useSite, useCurrency } from '../../context/SiteContext';
import { getSiteModels, getSitePackages, Q } from '../../api'; import { getSiteModels, getSitePackages, Q } from '../../api';
import { calcOfficialEquivList } from '../../utils/officialEquiv'; import { calcOfficialEquivList } from '../../utils/officialEquiv';
import RotatingEquiv from '../../components/bits/RotatingEquiv'; import RotatingEquiv from '../../components/bits/RotatingEquiv';
@@ -13,6 +13,7 @@ export default function MinimalHome() {
const { t } = useTranslation(); const { t } = useTranslation();
const { user } = useAuth(); const { user } = useAuth();
const { site } = useSite(); const { site } = useSite();
const { symbol, rate } = useCurrency();
const [models, setModels] = useState([]); const [models, setModels] = useState([]);
const [packages, setPackages] = useState([]); const [packages, setPackages] = useState([]);
@@ -154,9 +155,9 @@ export default function MinimalHome() {
<h3 className="text-base font-semibold text-white">{pkg.name}</h3> <h3 className="text-base font-semibold text-white">{pkg.name}</h3>
{pkg.description && <p className="text-sm text-neutral-500 mt-1">{pkg.description}</p>} {pkg.description && <p className="text-sm text-neutral-500 mt-1">{pkg.description}</p>}
<div className="mt-auto pt-6"> <div className="mt-auto pt-6">
<span className="text-3xl font-bold text-white">${pkg.price}</span> <span className="text-3xl font-bold text-white">{symbol}{(Number(pkg.price) * rate).toFixed(2)}</span>
{pkg.original_price > pkg.price && ( {pkg.original_price > pkg.price && (
<span className="text-sm text-neutral-600 line-through ml-2">${pkg.original_price}</span> <span className="text-sm text-neutral-600 line-through ml-2">{symbol}{(Number(pkg.original_price) * rate).toFixed(2)}</span>
)} )}
{pkg.duration > 0 && <p className="text-xs text-neutral-600 mt-1">{t('home.days', { count: pkg.duration })}</p>} {pkg.duration > 0 && <p className="text-xs text-neutral-600 mt-1">{t('home.days', { count: pkg.duration })}</p>}
</div> </div>
+4 -3
View File
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useAuth } from '../../context/AuthContext'; import { useAuth } from '../../context/AuthContext';
import { useSite } from '../../context/SiteContext'; import { useSite, useCurrency } from '../../context/SiteContext';
import { getSiteModels, getSitePackages, Q } from '../../api'; import { getSiteModels, getSitePackages, Q } from '../../api';
import { calcOfficialEquivList } from '../../utils/officialEquiv'; import { calcOfficialEquivList } from '../../utils/officialEquiv';
import RotatingEquiv from '../../components/bits/RotatingEquiv'; import RotatingEquiv from '../../components/bits/RotatingEquiv';
@@ -13,6 +13,7 @@ export default function StarterHome() {
const { t } = useTranslation(); const { t } = useTranslation();
const { user } = useAuth(); const { user } = useAuth();
const { site } = useSite(); const { site } = useSite();
const { symbol, rate } = useCurrency();
const [models, setModels] = useState([]); const [models, setModels] = useState([]);
const [packages, setPackages] = useState([]); const [packages, setPackages] = useState([]);
@@ -177,9 +178,9 @@ export default function StarterHome() {
<h3 className="text-base font-semibold text-white">{pkg.name}</h3> <h3 className="text-base font-semibold text-white">{pkg.name}</h3>
{pkg.description && <p className="text-sm text-neutral-500 mt-1">{pkg.description}</p>} {pkg.description && <p className="text-sm text-neutral-500 mt-1">{pkg.description}</p>}
<div className="mt-auto pt-6"> <div className="mt-auto pt-6">
<span className="text-3xl font-bold text-white">${pkg.price}</span> <span className="text-3xl font-bold text-white">{symbol}{(Number(pkg.price) * rate).toFixed(2)}</span>
{pkg.original_price > pkg.price && ( {pkg.original_price > pkg.price && (
<span className="text-sm text-neutral-600 line-through ml-2">${pkg.original_price}</span> <span className="text-sm text-neutral-600 line-through ml-2">{symbol}{(Number(pkg.original_price) * rate).toFixed(2)}</span>
)} )}
{pkg.duration > 0 && <p className="text-xs text-neutral-600 mt-1">{t('home.days', { count: pkg.duration })}</p>} {pkg.duration > 0 && <p className="text-xs text-neutral-600 mt-1">{t('home.days', { count: pkg.duration })}</p>}
</div> </div>