Fix critical and high priority issues from code review
Critical fixes: - Add defer to app.js for non-blocking loading - Add skip link and main landmark for accessibility - Fix ARIA tab state dynamic updates High priority fixes: - Add rel="noopener noreferrer" to external links - Implement non-blocking Google Fonts loading - Improve color contrast (text-slate-500 → text-slate-400) - Consolidate price data to single source (PRICING_DATA) - Add TELEGRAM_BOT_URL constant Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
42
app.js
42
app.js
@@ -3,6 +3,9 @@
|
||||
* 대화형 서버 런처 및 가격 데이터 관리
|
||||
*/
|
||||
|
||||
// 텔레그램 봇 URL 상수
|
||||
const TELEGRAM_BOT_URL = 'https://t.me/AnvilForgeBot';
|
||||
|
||||
// 단일 가격 데이터 소스 (VAT 포함, 월간 기준)
|
||||
const PRICING_DATA = {
|
||||
global: [
|
||||
@@ -20,21 +23,18 @@ const PRICING_DATA = {
|
||||
]
|
||||
};
|
||||
|
||||
// 런처 모달용 가격 (plan명 기준)
|
||||
const LAUNCHER_PRICES = {
|
||||
'Micro': { base: 8500, seoul: 8500 },
|
||||
'Starter': { base: 20400, seoul: 17000 },
|
||||
'Pro': { base: 40700, seoul: 33900 },
|
||||
'Business': { base: 67800, seoul: 67800 }
|
||||
};
|
||||
// 런처 모달용 가격 - PRICING_DATA에서 파생
|
||||
const LAUNCHER_PRICES = Object.fromEntries(
|
||||
PRICING_DATA.global.map(p => [p.plan, {
|
||||
base: p.price,
|
||||
seoul: PRICING_DATA.seoul.find(s => s.plan === p.plan)?.price || p.price
|
||||
}])
|
||||
);
|
||||
|
||||
// 플랜별 스펙 정보
|
||||
const PLAN_SPECS = {
|
||||
'Micro': '1vCPU / 1GB RAM',
|
||||
'Starter': '1vCPU / 2GB RAM',
|
||||
'Pro': '2vCPU / 4GB RAM',
|
||||
'Business': '4vCPU / 8GB RAM'
|
||||
};
|
||||
// 플랜별 스펙 정보 - PRICING_DATA에서 파생
|
||||
const PLAN_SPECS = Object.fromEntries(
|
||||
PRICING_DATA.global.map(p => [p.plan, `${p.vcpu} / ${p.ram} RAM`])
|
||||
);
|
||||
|
||||
// 리전 정보
|
||||
const REGIONS = [
|
||||
@@ -276,11 +276,25 @@ function switchTab(tab) {
|
||||
const panelTf = document.getElementById('panel-tf');
|
||||
|
||||
if (tab === 'n8n') {
|
||||
// Update ARIA states for n8n tab
|
||||
btnN8n.setAttribute('aria-selected', 'true');
|
||||
btnN8n.setAttribute('tabindex', '0');
|
||||
btnTf.setAttribute('aria-selected', 'false');
|
||||
btnTf.setAttribute('tabindex', '-1');
|
||||
|
||||
// Update classes
|
||||
btnN8n.className = 'px-4 py-2 rounded-lg bg-purple-600 text-white text-sm font-bold transition shadow-lg shadow-purple-500/20';
|
||||
btnTf.className = 'px-4 py-2 rounded-lg bg-slate-800 text-slate-400 text-sm font-bold border border-slate-700 hover:text-white transition';
|
||||
panelN8n.classList.remove('hidden');
|
||||
panelTf.classList.add('hidden');
|
||||
} else {
|
||||
// Update ARIA states for Terraform tab
|
||||
btnTf.setAttribute('aria-selected', 'true');
|
||||
btnTf.setAttribute('tabindex', '0');
|
||||
btnN8n.setAttribute('aria-selected', 'false');
|
||||
btnN8n.setAttribute('tabindex', '-1');
|
||||
|
||||
// Update classes
|
||||
btnN8n.className = 'px-4 py-2 rounded-lg bg-slate-800 text-slate-400 text-sm font-bold border border-slate-700 hover:text-white transition';
|
||||
btnTf.className = 'px-4 py-2 rounded-lg bg-blue-600 text-white text-sm font-bold transition shadow-lg shadow-blue-500/20';
|
||||
panelN8n.classList.add('hidden');
|
||||
|
||||
Reference in New Issue
Block a user