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');
|
||||
|
||||
37
index.html
37
index.html
@@ -36,16 +36,21 @@
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Pretendard:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Pretendard:wght@400;600;700&display=swap">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Pretendard:wght@400;600;700&display=swap" media="print" onload="this.media='all'">
|
||||
<noscript><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Pretendard:wght@400;600;700&display=swap"></noscript>
|
||||
|
||||
<!-- App JavaScript (must load before Alpine.js) -->
|
||||
<script src="app.js"></script>
|
||||
<script defer src="app.js"></script>
|
||||
|
||||
<!-- Alpine.js (pinned version with SRI) -->
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.3/dist/cdn.min.js" integrity="sha384-iZD2X8o1Zdq0HR5H/7oa8W30WS4No+zWCKUPD7fHRay9I1Gf+C4F8sVmw7zec1wW" crossorigin="anonymous"></script>
|
||||
</head>
|
||||
<body x-data="anvilApp()" @keydown.window="handleKeydown($event)" class="font-sans antialiased selection:bg-brand-500/30">
|
||||
|
||||
<!-- Skip Link for Keyboard Users -->
|
||||
<a href="#main-content" class="skip-link">메인 콘텐츠로 건너뛰기</a>
|
||||
|
||||
<!-- Navbar -->
|
||||
<nav class="fixed w-full z-50 top-0 border-b border-white/5 bg-dark-900/80 backdrop-blur-md">
|
||||
<div class="max-w-7xl mx-auto px-6 h-16 flex items-center justify-between">
|
||||
@@ -67,6 +72,9 @@
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main id="main-content">
|
||||
|
||||
<!-- Hero Section -->
|
||||
<section class="relative pt-32 pb-20 overflow-hidden grid-bg gradient-bg noise-bg">
|
||||
<!-- Animated Blobs -->
|
||||
@@ -159,7 +167,7 @@
|
||||
📍 <span class="text-white/90">위치:</span> 🇯🇵 도쿄<br>
|
||||
💿 <span class="text-white/90">OS:</span> Debian 12<br>
|
||||
⚡ <span class="text-white/90">사양:</span> Pro (2 vCPU / 4GB)
|
||||
<div class="text-[10px] text-slate-500 text-right mt-1">오후 3:42</div>
|
||||
<div class="text-[10px] text-slate-400 text-right mt-1">오후 3:42</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -179,7 +187,7 @@
|
||||
🌐 <span class="text-white/90">IP:</span> <code class="bg-black/30 px-1 rounded text-[13px]">45.12.89.121</code><br>
|
||||
🔑 <span class="text-white/90">Root PW:</span> <span class="text-slate-400">(보안 전송됨)</span><br><br>
|
||||
<span class="text-slate-400 text-[13px]">지금 바로 SSH 접속이 가능합니다.</span>
|
||||
<div class="text-[10px] text-slate-500 text-right mt-1">오후 3:43</div>
|
||||
<div class="text-[10px] text-slate-400 text-right mt-1">오후 3:43</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -281,7 +289,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-bold text-white">Webhook Integration</div>
|
||||
<div class="text-sm text-slate-500">Github Actions, GitLab CI/CD 연동</div>
|
||||
<div class="text-sm text-slate-400">Github Actions, GitLab CI/CD 연동</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex items-center gap-4">
|
||||
@@ -290,7 +298,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-bold text-white">Full API Access</div>
|
||||
<div class="text-sm text-slate-500">서버 생성, 제어, 모니터링 100% API 지원</div>
|
||||
<div class="text-sm text-slate-400">서버 생성, 제어, 모니터링 100% API 지원</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -496,7 +504,7 @@
|
||||
<span class="text-white">.it.com / .dev</span>
|
||||
<div class="text-right">
|
||||
<span class="text-brand-400 font-bold">₩8,700 / ₩27,800</span>
|
||||
<div class="text-[10px] text-slate-500 mt-0.5">(.it.com 갱신 ₩67,900)</div>
|
||||
<div class="text-[10px] text-slate-400 mt-0.5">(.it.com 갱신 ₩67,900)</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-between items-center p-3 border-b border-white/5">
|
||||
@@ -508,7 +516,7 @@
|
||||
<span class="text-brand-400 font-bold">₩27,800 / ₩31,300</span>
|
||||
</div>
|
||||
</div>
|
||||
<a href="https://t.me/AnvilForgeBot" target="_blank" class="mt-8 w-full py-3 bg-brand-600 hover:bg-brand-500 text-white font-bold rounded-lg transition text-center block">
|
||||
<a href="https://t.me/AnvilForgeBot" target="_blank" rel="noopener noreferrer" class="mt-8 w-full py-3 bg-brand-600 hover:bg-brand-500 text-white font-bold rounded-lg transition text-center block">
|
||||
🌐 도메인 검색 시작하기
|
||||
</a>
|
||||
</div>
|
||||
@@ -657,9 +665,9 @@
|
||||
<span class="text-sm text-slate-400">Live Inventory Pricing</span>
|
||||
<div class="text-4xl md:text-5xl font-bold text-white mt-1">₩169,000<span class="text-lg text-slate-500 font-normal">~</span></div>
|
||||
<p class="text-xs text-brand-400 mt-2">🇸🇬 싱가포르 최저가 재고 보유 중</p>
|
||||
<p class="text-[10px] text-slate-500 mt-1">※ 서울/도쿄 리전 재고 및 가격은 봇으로 확인하세요.</p>
|
||||
<p class="text-[10px] text-slate-400 mt-1">※ 서울/도쿄 리전 재고 및 가격은 봇으로 확인하세요.</p>
|
||||
</div>
|
||||
<a href="https://t.me/AnvilForgeBot" target="_blank" class="btn-glow px-8 py-4 bg-gradient-to-r from-brand-600 to-brand-500 text-white font-bold rounded-xl hover:shadow-lg hover:shadow-brand-500/30 transition-all flex items-center gap-3">
|
||||
<a href="https://t.me/AnvilForgeBot" target="_blank" rel="noopener noreferrer" class="btn-glow px-8 py-4 bg-gradient-to-r from-brand-600 to-brand-500 text-white font-bold rounded-xl hover:shadow-lg hover:shadow-brand-500/30 transition-all flex items-center gap-3">
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm4.64 6.8c-.15 1.58-.8 5.42-1.13 7.19-.14.75-.42 1-.68 1.03-.58.05-1.02-.38-1.58-.75-.88-.58-1.38-.94-2.23-1.5-.99-.65-.35-1.01.22-1.59.15-.15 2.71-2.48 2.76-2.69a.2.2 0 00-.05-.18c-.06-.05-.14-.03-.21-.02-.09.02-1.49.95-4.22 2.79-.4.27-.76.41-1.08.4-.36-.01-1.04-.2-1.55-.37-.62-.2-1.12-.31-1.15-.63.03-.37.59-.75 1.5-.95 6.07-2.64 10.12-4.38 12.15-5.21 2.91-1.2 3.51-1.4 3.91-1.41.09 0 .28.02.41.09.11.06.23.14.3.24.08.12.12.33.09.57z"/></svg>
|
||||
실시간 재고/가격 조회 (Bot)
|
||||
</a>
|
||||
@@ -669,6 +677,9 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
<!-- End Main Content -->
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="py-16 relative overflow-hidden">
|
||||
<!-- Background -->
|
||||
@@ -696,13 +707,13 @@
|
||||
<!-- Company Info -->
|
||||
<div class="text-center md:text-right text-sm">
|
||||
<div class="text-slate-400">© 2026 LIBEHAIM Inc. | Taro Tanaka</div>
|
||||
<div class="text-xs text-slate-600 mt-1">#202 K-Flat, 3-1-13 Higashioi, Shinagawa-ku, Tokyo 140-0011, Japan</div>
|
||||
<div class="text-xs text-slate-500 mt-1">#202 K-Flat, 3-1-13 Higashioi, Shinagawa-ku, Tokyo 140-0011, Japan</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bottom Decoration -->
|
||||
<div class="mt-12 pt-8 border-t border-white/5 text-center">
|
||||
<p class="text-xs text-slate-600">Powered by <span class="text-brand-400">Cloud Infrastructure</span> across 4 global regions</p>
|
||||
<p class="text-xs text-slate-500">Powered by <span class="text-brand-400">Cloud Infrastructure</span> across 4 global regions</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -906,7 +917,7 @@
|
||||
<p class="text-slate-400 text-xs mb-3">인스턴스가 활성화되었습니다</p>
|
||||
<div class="flex gap-2">
|
||||
<button @click="resetLauncher" class="flex-1 py-2.5 bg-slate-800 hover:bg-slate-700 text-white font-semibold rounded-xl transition text-sm">닫기</button>
|
||||
<a href="https://t.me/AnvilForgeBot" target="_blank" class="flex-1 py-2.5 bg-gradient-to-r from-brand-600 to-brand-500 text-white font-semibold rounded-xl transition text-center text-sm">Console →</a>
|
||||
<a href="https://t.me/AnvilForgeBot" target="_blank" rel="noopener noreferrer" class="flex-1 py-2.5 bg-gradient-to-r from-brand-600 to-brand-500 text-white font-semibold rounded-xl transition text-center text-sm">Console →</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user