Files
rikkei_simple_care/landing_page/index.html
PhuocNTB 7a9c7a93ff
All checks were successful
Deploy on Master Change / deploy (push) Successful in 40s
fix
2026-07-01 15:04:56 +07:00

406 lines
13 KiB
HTML

<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simple Care - Tải Ứng Dụng & Hướng Dẫn Hỗ Trợ Sinh Viên</title>
<style>
:root {
--primary: #bb2126;
--primary-hover: #9c1b1f;
--bg: #f8fafc;
--card-bg: #ffffff;
--text-main: #1e293b;
--text-sub: #475569;
--border: #e2e8f0;
--radius: 8px;
--transition: all 0.2s ease;
--shadow: 0 1px 3px rgba(0,0,0,0.05), 0 1px 2px rgba(0,0,0,0.03);
--font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: var(--font);
background-color: var(--bg);
color: var(--text-main);
line-height: 1.5;
padding: 2rem 1rem;
}
.container {
max-width: 800px;
margin: 0 auto;
}
header {
text-align: center;
margin-bottom: 2.5rem;
padding-bottom: 1.5rem;
border-bottom: 1px solid var(--border);
}
h1 {
font-size: 2rem;
color: var(--primary);
margin-bottom: 0.5rem;
font-weight: 800;
}
.subtitle {
color: var(--text-sub);
font-size: 1rem;
}
section {
margin-bottom: 2.5rem;
}
h2 {
font-size: 1.25rem;
margin-bottom: 1.25rem;
color: var(--text-main);
display: flex;
align-items: center;
gap: 8px;
}
.grid {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
}
@media (min-width: 600px) {
.grid {
grid-template-columns: 1fr 1fr;
}
}
.card {
background: var(--card-bg);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 1.25rem;
box-shadow: var(--shadow);
display: flex;
flex-direction: column;
justify-content: space-between;
transition: var(--transition);
}
.card:hover {
border-color: var(--primary);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
}
.card-title {
font-weight: 700;
font-size: 1.05rem;
margin-bottom: 0.25rem;
display: flex;
align-items: center;
gap: 8px;
}
.card-desc {
color: var(--text-sub);
font-size: 0.85rem;
margin-bottom: 1.25rem;
flex-grow: 1;
}
.platform-badge {
display: inline-flex;
align-items: center;
padding: 0.2rem 0.5rem;
border-radius: 4px;
font-size: 0.75rem;
font-weight: 600;
background: #f1f5f9;
color: var(--text-sub);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
background-color: var(--primary);
color: #fff;
text-decoration: none;
padding: 0.65rem 1rem;
border-radius: var(--radius);
font-weight: 600;
font-size: 0.9rem;
transition: var(--transition);
text-align: center;
border: none;
cursor: pointer;
}
.btn:hover {
background-color: var(--primary-hover);
}
/* Guides section styles */
.guide-list {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.guide-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.85rem 1.25rem;
background: var(--card-bg);
border: 1px solid var(--border);
border-radius: var(--radius);
box-shadow: var(--shadow);
transition: var(--transition);
}
.guide-item:hover {
border-color: var(--primary);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
}
.guide-link {
display: flex;
align-items: center;
gap: 10px;
color: var(--text-main);
text-decoration: none;
font-weight: 600;
font-size: 0.95rem;
flex-grow: 1;
}
.guide-link:hover {
color: var(--primary);
}
.guide-link-btn {
font-size: 0.85rem;
color: var(--primary);
font-weight: 600;
text-decoration: none;
}
.guide-link-btn:hover {
text-decoration: underline;
}
.loading-container, .error-container {
text-align: center;
padding: 3rem 1.5rem;
background: var(--card-bg);
border: 1px solid var(--border);
border-radius: var(--radius);
color: var(--text-sub);
}
.spinner {
width: 32px;
height: 32px;
border: 3px solid #e2e8f0;
border-top-color: var(--primary);
border-radius: 50%;
animation: spin 0.8s linear infinite;
margin: 0 auto 1rem;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
footer {
text-align: center;
margin-top: 4rem;
padding-top: 1.5rem;
border-top: 1px solid var(--border);
color: var(--text-sub);
font-size: 0.8rem;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>Simple Care</h1>
<p class="subtitle">Cổng tải ứng dụng và tài liệu hướng dẫn học tập, thi cử dành cho sinh viên</p>
</header>
<main>
<!-- Applications section -->
<section>
<h2>
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style="color: var(--primary)">
<rect x="4" y="4" width="16" height="16" rx="2" ry="2" />
<path d="M9 22V12h6v10M2 17l10 5 10-5M2 12l10 5 10-5" />
</svg>
Ứng dụng hỗ trợ
</h2>
<div id="apps-loading" class="loading-container">
<div class="spinner"></div>
<p>Đang tải danh sách ứng dụng...</p>
</div>
<div id="apps-error" class="error-container" style="display: none;"></div>
<div id="apps-list" class="grid" style="display: none;"></div>
</section>
<!-- Guides section -->
<section>
<h2>
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style="color: var(--primary)">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
<polyline points="14 2 14 8 20 8" />
<line x1="16" y1="13" x2="8" y2="13" />
<line x1="16" y1="17" x2="8" y2="17" />
</svg>
Hướng dẫn & Tài liệu
</h2>
<div id="guides-loading" class="loading-container">
<div class="spinner"></div>
<p>Đang tải danh sách tài liệu...</p>
</div>
<div id="guides-error" class="error-container" style="display: none;"></div>
<div id="guides-list" class="guide-list" style="display: none;"></div>
</section>
</main>
<footer>
<p>&copy; 2026 Simple Care. Bản quyền thuộc Rikkei Academy.</p>
</footer>
</div>
<script>
// Platforms coloring/badges
function getPlatformIcon(platform) {
const p = platform.toLowerCase();
if (p.includes('win')) {
return `<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor" style="color: #0078d7"><path d="M0 3.449L9.75 2.1v9.45H0V3.449zM0 12.45h9.75v9.45L0 20.551v-8.1zM10.8 1.95L24 0v11.55H10.8V1.95zM10.8 12.45H24v11.55l-13.2-1.95v-9.6z"/></svg>`;
}
if (p.includes('mac') || p.includes('apple') || p.includes('ios')) {
return `<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor" style="color: #555"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.81-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M15.97 4.17c.66-.81 1.11-1.93.99-3.05-1 .04-2.22.67-2.94 1.51-.62.73-1.16 1.87-1.01 2.97 1.12.09 2.27-.58 2.96-1.43z"/></svg>`;
}
if (p.includes('linux')) {
return `<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor" style="color: #FCC624"><path d="M12 2c-3.3 0-6 2.7-6 6 0 2.2.8 4.2 2.2 5.6C6.5 15.6 5 18.5 5 22h14c0-3.5-1.5-6.4-3.2-8.4 1.4-1.4 2.2-3.4 2.2-5.6 0-3.3-2.7-6-6-6zm0 2c2.2 0 4 1.8 4 4 0 .9-.3 1.7-.8 2.3-.3.4-.7.7-1.2.9-.6.2-1.3.3-2 .3s-1.4-.1-2-.3c-.5-.2-.9-.5-1.2-.9-.5-.6-.8-1.4-.8-2.3 0-2.2 1.8-4 4-4z"/></svg>`;
}
return `<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style="color: var(--primary)"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>`;
}
const host = 'https://sv.rikkeiraia.org';
async function loadApps() {
const loader = document.getElementById('apps-loading');
const errorDiv = document.getElementById('apps-error');
const listDiv = document.getElementById('apps-list');
try {
const response = await fetch(`${host}/api/public/app-downloads`);
if (!response.ok) throw new Error('Không thể lấy danh sách ứng dụng');
const res = await response.json();
const data = res.data || [];
loader.style.display = 'none';
if (data.length === 0) {
errorDiv.innerText = 'Không có ứng dụng nào khả dụng để tải về.';
errorDiv.style.display = 'block';
return;
}
listDiv.innerHTML = data.map(app => `
<div class="card">
<div>
<div class="card-title">
${getPlatformIcon(app.platform)}
${app.name}
</div>
<div class="card-desc">${app.description || 'Hỗ trợ quá trình kiểm tra và học tập.'}</div>
</div>
<div style="display:flex; justify-content:space-between; align-items:center;">
<span class="platform-badge">${app.platform === 'macOS' ? 'MacOS' : app.platform}</span>
<a href="${app.downloadUrl}" target="_blank" rel="noopener noreferrer" class="btn">
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
<polyline points="7 10 12 15 17 10"/>
<line x1="12" y1="15" x2="12" y2="3"/>
</svg>
Tải về
</a>
</div>
</div>
`).join('');
listDiv.style.display = 'grid';
} catch (err) {
loader.style.display = 'none';
errorDiv.innerText = 'Lỗi kết nối máy chủ. Vui lòng thử lại sau.';
errorDiv.style.display = 'block';
}
}
async function loadGuides() {
const loader = document.getElementById('guides-loading');
const errorDiv = document.getElementById('guides-error');
const listDiv = document.getElementById('guides-list');
try {
const response = await fetch(`${host}/api/public/guide-links`);
if (!response.ok) throw new Error('Không thể lấy danh sách hướng dẫn');
const res = await response.json();
const data = res.data || [];
loader.style.display = 'none';
if (data.length === 0) {
errorDiv.innerText = 'Không có tài liệu hoặc video hướng dẫn nào.';
errorDiv.style.display = 'block';
return;
}
listDiv.innerHTML = data.map(guide => {
const isVideo = guide.url.toLowerCase().includes('youtube.com') || guide.url.toLowerCase().includes('youtu.be') || guide.url.toLowerCase().includes('drive.google.com/file');
const icon = isVideo
? `<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style="color: #ea4335"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>`
: `<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style="color: #4285f4"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>`;
return `
<div class="guide-item">
<a href="${guide.url}" target="_blank" rel="noopener noreferrer" class="guide-link">
${icon}
${guide.title}
</a>
<a href="${guide.url}" target="_blank" rel="noopener noreferrer" class="guide-link-btn">Xem &rarr;</a>
</div>
`;
}).join('');
listDiv.style.display = 'flex';
} catch (err) {
loader.style.display = 'none';
errorDiv.innerText = 'Lỗi kết nối máy chủ. Vui lòng thử lại sau.';
errorDiv.style.display = 'block';
}
}
// Run
loadApps();
loadGuides();
</script>
</body>
</html>